diff options
Diffstat (limited to 'src')
334 files changed, 18256 insertions, 12898 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 63dd9febf..19d16147d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -24,6 +24,7 @@ if (MSVC) | |||
| 24 | # /W3 - Level 3 warnings | 24 | # /W3 - Level 3 warnings |
| 25 | # /MP - Multi-threaded compilation | 25 | # /MP - Multi-threaded compilation |
| 26 | # /Zi - Output debugging information | 26 | # /Zi - Output debugging information |
| 27 | # /Zm - Specifies the precompiled header memory allocation limit | ||
| 27 | # /Zo - Enhanced debug info for optimized builds | 28 | # /Zo - Enhanced debug info for optimized builds |
| 28 | # /permissive- - Enables stricter C++ standards conformance checks | 29 | # /permissive- - Enables stricter C++ standards conformance checks |
| 29 | # /EHsc - C++-only exception handling semantics | 30 | # /EHsc - C++-only exception handling semantics |
| @@ -36,6 +37,7 @@ if (MSVC) | |||
| 36 | add_compile_options( | 37 | add_compile_options( |
| 37 | /MP | 38 | /MP |
| 38 | /Zi | 39 | /Zi |
| 40 | /Zm200 | ||
| 39 | /Zo | 41 | /Zo |
| 40 | /permissive- | 42 | /permissive- |
| 41 | /EHsc | 43 | /EHsc |
diff --git a/src/audio_core/delay_line.cpp b/src/audio_core/delay_line.cpp index f4e4dd8d2..2793ed8db 100644 --- a/src/audio_core/delay_line.cpp +++ b/src/audio_core/delay_line.cpp | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #include <cstring> | 5 | #include <cstring> |
| 2 | #include "audio_core/delay_line.h" | 6 | #include "audio_core/delay_line.h" |
| 3 | 7 | ||
diff --git a/src/audio_core/delay_line.h b/src/audio_core/delay_line.h index cafddd432..84f11bc52 100644 --- a/src/audio_core/delay_line.h +++ b/src/audio_core/delay_line.h | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #pragma once | 5 | #pragma once |
| 2 | 6 | ||
| 3 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 23d43a394..919da4a53 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -73,6 +73,7 @@ add_library(common STATIC | |||
| 73 | hex_util.h | 73 | hex_util.h |
| 74 | host_memory.cpp | 74 | host_memory.cpp |
| 75 | host_memory.h | 75 | host_memory.h |
| 76 | input.h | ||
| 76 | intrusive_red_black_tree.h | 77 | intrusive_red_black_tree.h |
| 77 | literals.h | 78 | literals.h |
| 78 | logging/backend.cpp | 79 | logging/backend.cpp |
diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 64520ca4e..eef8c1c5a 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <bit> | 7 | #include <bit> |
| 8 | #include <climits> | 8 | #include <climits> |
| 9 | #include <cstddef> | 9 | #include <cstddef> |
| 10 | #include <type_traits> | ||
| 10 | 11 | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | 13 | ||
| @@ -44,4 +45,10 @@ template <typename T> | |||
| 44 | return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL)); | 45 | return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL)); |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 48 | template <typename T> | ||
| 49 | requires std::is_integral_v<T> | ||
| 50 | [[nodiscard]] T NextPow2(T value) { | ||
| 51 | return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); | ||
| 52 | } | ||
| 53 | |||
| 47 | } // namespace Common | 54 | } // namespace Common |
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index b44a44949..28949fe5e 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #ifdef _WIN32 | 5 | #ifdef _WIN32 |
| 2 | 6 | ||
| 3 | #include <iterator> | 7 | #include <iterator> |
diff --git a/src/common/input.h b/src/common/input.h new file mode 100644 index 000000000..f775a4c01 --- /dev/null +++ b/src/common/input.h | |||
| @@ -0,0 +1,366 @@ | |||
| 1 | // Copyright 2017 Citra 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 <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <unordered_map> | ||
| 11 | #include <utility> | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/param_package.h" | ||
| 14 | #include "common/uuid.h" | ||
| 15 | |||
| 16 | namespace Common::Input { | ||
| 17 | |||
| 18 | // Type of data that is expected to recieve or send | ||
| 19 | enum class InputType { | ||
| 20 | None, | ||
| 21 | Battery, | ||
| 22 | Button, | ||
| 23 | Stick, | ||
| 24 | Analog, | ||
| 25 | Trigger, | ||
| 26 | Motion, | ||
| 27 | Touch, | ||
| 28 | Color, | ||
| 29 | Vibration, | ||
| 30 | Nfc, | ||
| 31 | Ir, | ||
| 32 | }; | ||
| 33 | |||
| 34 | // Internal battery charge level | ||
| 35 | enum class BatteryLevel : u32 { | ||
| 36 | None, | ||
| 37 | Empty, | ||
| 38 | Critical, | ||
| 39 | Low, | ||
| 40 | Medium, | ||
| 41 | Full, | ||
| 42 | Charging, | ||
| 43 | }; | ||
| 44 | |||
| 45 | enum class PollingMode { | ||
| 46 | // Constant polling of buttons, analogs and motion data | ||
| 47 | Active, | ||
| 48 | // Only update on button change, digital analogs | ||
| 49 | Pasive, | ||
| 50 | // Enable near field communication polling | ||
| 51 | NFC, | ||
| 52 | // Enable infrared camera polling | ||
| 53 | IR, | ||
| 54 | }; | ||
| 55 | |||
| 56 | // Vibration reply from the controller | ||
| 57 | enum class VibrationError { | ||
| 58 | None, | ||
| 59 | NotSupported, | ||
| 60 | Disabled, | ||
| 61 | Unknown, | ||
| 62 | }; | ||
| 63 | |||
| 64 | // Polling mode reply from the controller | ||
| 65 | enum class PollingError { | ||
| 66 | None, | ||
| 67 | NotSupported, | ||
| 68 | Unknown, | ||
| 69 | }; | ||
| 70 | |||
| 71 | // Hint for amplification curve to be used | ||
| 72 | enum class VibrationAmplificationType { | ||
| 73 | Linear, | ||
| 74 | Exponential, | ||
| 75 | }; | ||
| 76 | |||
| 77 | // Analog properties for calibration | ||
| 78 | struct AnalogProperties { | ||
| 79 | // Anything below this value will be detected as zero | ||
| 80 | float deadzone{}; | ||
| 81 | // Anyting above this values will be detected as one | ||
| 82 | float range{1.0f}; | ||
| 83 | // Minimum value to be detected as active | ||
| 84 | float threshold{0.5f}; | ||
| 85 | // Drift correction applied to the raw data | ||
| 86 | float offset{}; | ||
| 87 | // Invert direction of the sensor data | ||
| 88 | bool inverted{}; | ||
| 89 | }; | ||
| 90 | |||
| 91 | // Single analog sensor data | ||
| 92 | struct AnalogStatus { | ||
| 93 | float value{}; | ||
| 94 | float raw_value{}; | ||
| 95 | AnalogProperties properties{}; | ||
| 96 | }; | ||
| 97 | |||
| 98 | // Button data | ||
| 99 | struct ButtonStatus { | ||
| 100 | Common::UUID uuid{}; | ||
| 101 | bool value{}; | ||
| 102 | bool inverted{}; | ||
| 103 | bool toggle{}; | ||
| 104 | bool locked{}; | ||
| 105 | }; | ||
| 106 | |||
| 107 | // Internal battery data | ||
| 108 | using BatteryStatus = BatteryLevel; | ||
| 109 | |||
| 110 | // Analog and digital joystick data | ||
| 111 | struct StickStatus { | ||
| 112 | Common::UUID uuid{}; | ||
| 113 | AnalogStatus x{}; | ||
| 114 | AnalogStatus y{}; | ||
| 115 | bool left{}; | ||
| 116 | bool right{}; | ||
| 117 | bool up{}; | ||
| 118 | bool down{}; | ||
| 119 | }; | ||
| 120 | |||
| 121 | // Analog and digital trigger data | ||
| 122 | struct TriggerStatus { | ||
| 123 | Common::UUID uuid{}; | ||
| 124 | AnalogStatus analog{}; | ||
| 125 | ButtonStatus pressed{}; | ||
| 126 | }; | ||
| 127 | |||
| 128 | // 3D vector representing motion input | ||
| 129 | struct MotionSensor { | ||
| 130 | AnalogStatus x{}; | ||
| 131 | AnalogStatus y{}; | ||
| 132 | AnalogStatus z{}; | ||
| 133 | }; | ||
| 134 | |||
| 135 | // Motion data used to calculate controller orientation | ||
| 136 | struct MotionStatus { | ||
| 137 | // Gyroscope vector measurement in radians/s. | ||
| 138 | MotionSensor gyro{}; | ||
| 139 | // Acceleration vector measurement in G force | ||
| 140 | MotionSensor accel{}; | ||
| 141 | // Time since last measurement in microseconds | ||
| 142 | u64 delta_timestamp{}; | ||
| 143 | // Request to update after reading the value | ||
| 144 | bool force_update{}; | ||
| 145 | }; | ||
| 146 | |||
| 147 | // Data of a single point on a touch screen | ||
| 148 | struct TouchStatus { | ||
| 149 | ButtonStatus pressed{}; | ||
| 150 | AnalogStatus x{}; | ||
| 151 | AnalogStatus y{}; | ||
| 152 | int id{}; | ||
| 153 | }; | ||
| 154 | |||
| 155 | // Physical controller color in RGB format | ||
| 156 | struct BodyColorStatus { | ||
| 157 | u32 body{}; | ||
| 158 | u32 buttons{}; | ||
| 159 | }; | ||
| 160 | |||
| 161 | // HD rumble data | ||
| 162 | struct VibrationStatus { | ||
| 163 | f32 low_amplitude{}; | ||
| 164 | f32 low_frequency{}; | ||
| 165 | f32 high_amplitude{}; | ||
| 166 | f32 high_frequency{}; | ||
| 167 | VibrationAmplificationType type; | ||
| 168 | }; | ||
| 169 | |||
| 170 | // Physical controller LED pattern | ||
| 171 | struct LedStatus { | ||
| 172 | bool led_1{}; | ||
| 173 | bool led_2{}; | ||
| 174 | bool led_3{}; | ||
| 175 | bool led_4{}; | ||
| 176 | }; | ||
| 177 | |||
| 178 | // List of buttons to be passed to Qt that can be translated | ||
| 179 | enum class ButtonNames { | ||
| 180 | Undefined, | ||
| 181 | Invalid, | ||
| 182 | // This will display the engine name instead of the button name | ||
| 183 | Engine, | ||
| 184 | // This will display the button by value instead of the button name | ||
| 185 | Value, | ||
| 186 | ButtonLeft, | ||
| 187 | ButtonRight, | ||
| 188 | ButtonDown, | ||
| 189 | ButtonUp, | ||
| 190 | TriggerZ, | ||
| 191 | TriggerR, | ||
| 192 | TriggerL, | ||
| 193 | ButtonA, | ||
| 194 | ButtonB, | ||
| 195 | ButtonX, | ||
| 196 | ButtonY, | ||
| 197 | ButtonStart, | ||
| 198 | |||
| 199 | // DS4 button names | ||
| 200 | L1, | ||
| 201 | L2, | ||
| 202 | L3, | ||
| 203 | R1, | ||
| 204 | R2, | ||
| 205 | R3, | ||
| 206 | Circle, | ||
| 207 | Cross, | ||
| 208 | Square, | ||
| 209 | Triangle, | ||
| 210 | Share, | ||
| 211 | Options, | ||
| 212 | }; | ||
| 213 | |||
| 214 | // Callback data consisting of an input type and the equivalent data status | ||
| 215 | struct CallbackStatus { | ||
| 216 | InputType type{InputType::None}; | ||
| 217 | ButtonStatus button_status{}; | ||
| 218 | StickStatus stick_status{}; | ||
| 219 | AnalogStatus analog_status{}; | ||
| 220 | TriggerStatus trigger_status{}; | ||
| 221 | MotionStatus motion_status{}; | ||
| 222 | TouchStatus touch_status{}; | ||
| 223 | BodyColorStatus color_status{}; | ||
| 224 | BatteryStatus battery_status{}; | ||
| 225 | VibrationStatus vibration_status{}; | ||
| 226 | }; | ||
| 227 | |||
| 228 | // Triggered once every input change | ||
| 229 | struct InputCallback { | ||
| 230 | std::function<void(const CallbackStatus&)> on_change; | ||
| 231 | }; | ||
| 232 | |||
| 233 | /// An abstract class template for an input device (a button, an analog input, etc.). | ||
| 234 | class InputDevice { | ||
| 235 | public: | ||
| 236 | virtual ~InputDevice() = default; | ||
| 237 | |||
| 238 | // Request input device to update if necessary | ||
| 239 | virtual void SoftUpdate() {} | ||
| 240 | |||
| 241 | // Force input device to update data regardless of the current state | ||
| 242 | virtual void ForceUpdate() {} | ||
| 243 | |||
| 244 | // Sets the function to be triggered when input changes | ||
| 245 | void SetCallback(InputCallback callback_) { | ||
| 246 | callback = std::move(callback_); | ||
| 247 | } | ||
| 248 | |||
| 249 | // Triggers the function set in the callback | ||
| 250 | void TriggerOnChange(const CallbackStatus& status) { | ||
| 251 | if (callback.on_change) { | ||
| 252 | callback.on_change(status); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | private: | ||
| 257 | InputCallback callback; | ||
| 258 | }; | ||
| 259 | |||
| 260 | /// An abstract class template for an output device (rumble, LED pattern, polling mode). | ||
| 261 | class OutputDevice { | ||
| 262 | public: | ||
| 263 | virtual ~OutputDevice() = default; | ||
| 264 | |||
| 265 | virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {} | ||
| 266 | |||
| 267 | virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { | ||
| 268 | return VibrationError::NotSupported; | ||
| 269 | } | ||
| 270 | |||
| 271 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { | ||
| 272 | return PollingError::NotSupported; | ||
| 273 | } | ||
| 274 | }; | ||
| 275 | |||
| 276 | /// An abstract class template for a factory that can create input devices. | ||
| 277 | template <typename InputDeviceType> | ||
| 278 | class Factory { | ||
| 279 | public: | ||
| 280 | virtual ~Factory() = default; | ||
| 281 | virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0; | ||
| 282 | }; | ||
| 283 | |||
| 284 | namespace Impl { | ||
| 285 | |||
| 286 | template <typename InputDeviceType> | ||
| 287 | using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>; | ||
| 288 | |||
| 289 | template <typename InputDeviceType> | ||
| 290 | struct FactoryList { | ||
| 291 | static FactoryListType<InputDeviceType> list; | ||
| 292 | }; | ||
| 293 | |||
| 294 | template <typename InputDeviceType> | ||
| 295 | FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list; | ||
| 296 | |||
| 297 | } // namespace Impl | ||
| 298 | |||
| 299 | /** | ||
| 300 | * Registers an input device factory. | ||
| 301 | * @tparam InputDeviceType the type of input devices the factory can create | ||
| 302 | * @param name the name of the factory. Will be used to match the "engine" parameter when creating | ||
| 303 | * a device | ||
| 304 | * @param factory the factory object to register | ||
| 305 | */ | ||
| 306 | template <typename InputDeviceType> | ||
| 307 | void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) { | ||
| 308 | auto pair = std::make_pair(name, std::move(factory)); | ||
| 309 | if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) { | ||
| 310 | LOG_ERROR(Input, "Factory '{}' already registered", name); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | /** | ||
| 315 | * Unregisters an input device factory. | ||
| 316 | * @tparam InputDeviceType the type of input devices the factory can create | ||
| 317 | * @param name the name of the factory to unregister | ||
| 318 | */ | ||
| 319 | template <typename InputDeviceType> | ||
| 320 | void UnregisterFactory(const std::string& name) { | ||
| 321 | if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) { | ||
| 322 | LOG_ERROR(Input, "Factory '{}' not registered", name); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | /** | ||
| 327 | * Create an input device from given paramters. | ||
| 328 | * @tparam InputDeviceType the type of input devices to create | ||
| 329 | * @param params a serialized ParamPackage string that contains all parameters for creating the | ||
| 330 | * device | ||
| 331 | */ | ||
| 332 | template <typename InputDeviceType> | ||
| 333 | std::unique_ptr<InputDeviceType> CreateDeviceFromString(const std::string& params) { | ||
| 334 | const Common::ParamPackage package(params); | ||
| 335 | const std::string engine = package.Get("engine", "null"); | ||
| 336 | const auto& factory_list = Impl::FactoryList<InputDeviceType>::list; | ||
| 337 | const auto pair = factory_list.find(engine); | ||
| 338 | if (pair == factory_list.end()) { | ||
| 339 | if (engine != "null") { | ||
| 340 | LOG_ERROR(Input, "Unknown engine name: {}", engine); | ||
| 341 | } | ||
| 342 | return std::make_unique<InputDeviceType>(); | ||
| 343 | } | ||
| 344 | return pair->second->Create(package); | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * Create an input device from given paramters. | ||
| 349 | * @tparam InputDeviceType the type of input devices to create | ||
| 350 | * @param A ParamPackage that contains all parameters for creating the device | ||
| 351 | */ | ||
| 352 | template <typename InputDeviceType> | ||
| 353 | std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package) { | ||
| 354 | const std::string engine = package.Get("engine", "null"); | ||
| 355 | const auto& factory_list = Impl::FactoryList<InputDeviceType>::list; | ||
| 356 | const auto pair = factory_list.find(engine); | ||
| 357 | if (pair == factory_list.end()) { | ||
| 358 | if (engine != "null") { | ||
| 359 | LOG_ERROR(Input, "Unknown engine name: {}", engine); | ||
| 360 | } | ||
| 361 | return std::make_unique<InputDeviceType>(); | ||
| 362 | } | ||
| 363 | return pair->second->Create(package); | ||
| 364 | } | ||
| 365 | |||
| 366 | } // namespace Common::Input | ||
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 42744c994..b898a652c 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp | |||
| @@ -114,6 +114,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { | |||
| 114 | SUB(Service, NGCT) \ | 114 | SUB(Service, NGCT) \ |
| 115 | SUB(Service, NIFM) \ | 115 | SUB(Service, NIFM) \ |
| 116 | SUB(Service, NIM) \ | 116 | SUB(Service, NIM) \ |
| 117 | SUB(Service, NOTIF) \ | ||
| 117 | SUB(Service, NPNS) \ | 118 | SUB(Service, NPNS) \ |
| 118 | SUB(Service, NS) \ | 119 | SUB(Service, NS) \ |
| 119 | SUB(Service, NVDRV) \ | 120 | SUB(Service, NVDRV) \ |
diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 2d21fc483..9ed0c7ad6 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h | |||
| @@ -82,6 +82,7 @@ enum class Class : u8 { | |||
| 82 | Service_NGCT, ///< The NGCT (No Good Content for Terra) service | 82 | Service_NGCT, ///< The NGCT (No Good Content for Terra) service |
| 83 | Service_NIFM, ///< The NIFM (Network interface) service | 83 | Service_NIFM, ///< The NIFM (Network interface) service |
| 84 | Service_NIM, ///< The NIM service | 84 | Service_NIM, ///< The NIM service |
| 85 | Service_NOTIF, ///< The NOTIF (Notification) service | ||
| 85 | Service_NPNS, ///< The NPNS service | 86 | Service_NPNS, ///< The NPNS service |
| 86 | Service_NS, ///< The NS services | 87 | Service_NS, ///< The NS services |
| 87 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service | 88 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 3bcaa072f..6964a8273 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -183,6 +183,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 183 | values.max_anisotropy.SetGlobal(true); | 183 | values.max_anisotropy.SetGlobal(true); |
| 184 | values.use_speed_limit.SetGlobal(true); | 184 | values.use_speed_limit.SetGlobal(true); |
| 185 | values.speed_limit.SetGlobal(true); | 185 | values.speed_limit.SetGlobal(true); |
| 186 | values.fps_cap.SetGlobal(true); | ||
| 186 | values.use_disk_shader_cache.SetGlobal(true); | 187 | values.use_disk_shader_cache.SetGlobal(true); |
| 187 | values.gpu_accuracy.SetGlobal(true); | 188 | values.gpu_accuracy.SetGlobal(true); |
| 188 | values.use_asynchronous_gpu_emulation.SetGlobal(true); | 189 | values.use_asynchronous_gpu_emulation.SetGlobal(true); |
diff --git a/src/common/settings.h b/src/common/settings.h index 42f8b4a7d..313f1fa7f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <algorithm> | 7 | #include <algorithm> |
| 8 | #include <array> | 8 | #include <array> |
| 9 | #include <atomic> | ||
| 10 | #include <map> | 9 | #include <map> |
| 11 | #include <optional> | 10 | #include <optional> |
| 12 | #include <string> | 11 | #include <string> |
| @@ -525,7 +524,7 @@ struct Values { | |||
| 525 | Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; | 524 | Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; |
| 526 | Setting<bool> accelerate_astc{true, "accelerate_astc"}; | 525 | Setting<bool> accelerate_astc{true, "accelerate_astc"}; |
| 527 | Setting<bool> use_vsync{true, "use_vsync"}; | 526 | Setting<bool> use_vsync{true, "use_vsync"}; |
| 528 | BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; | 527 | RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; |
| 529 | BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; | 528 | BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; |
| 530 | RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, | 529 | RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, |
| 531 | ShaderBackend::SPIRV, "shader_backend"}; | 530 | ShaderBackend::SPIRV, "shader_backend"}; |
| @@ -560,25 +559,19 @@ struct Values { | |||
| 560 | Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; | 559 | Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; |
| 561 | 560 | ||
| 562 | Setting<bool> motion_enabled{true, "motion_enabled"}; | 561 | Setting<bool> motion_enabled{true, "motion_enabled"}; |
| 563 | BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01", | ||
| 564 | "motion_device"}; | ||
| 565 | BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; | 562 | BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; |
| 563 | BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"}; | ||
| 566 | 564 | ||
| 567 | BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; | 565 | BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; |
| 568 | BasicSetting<bool> tas_enable{false, "tas_enable"}; | 566 | BasicSetting<bool> tas_enable{false, "tas_enable"}; |
| 569 | BasicSetting<bool> tas_loop{false, "tas_loop"}; | 567 | BasicSetting<bool> tas_loop{false, "tas_loop"}; |
| 570 | BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"}; | ||
| 571 | 568 | ||
| 572 | BasicSetting<bool> mouse_panning{false, "mouse_panning"}; | 569 | BasicSetting<bool> mouse_panning{false, "mouse_panning"}; |
| 573 | BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; | 570 | BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; |
| 574 | BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; | 571 | BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; |
| 575 | std::string mouse_device; | ||
| 576 | MouseButtonsRaw mouse_buttons; | ||
| 577 | 572 | ||
| 578 | BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; | 573 | BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; |
| 579 | BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; | 574 | BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; |
| 580 | KeyboardKeysRaw keyboard_keys; | ||
| 581 | KeyboardModsRaw keyboard_mods; | ||
| 582 | 575 | ||
| 583 | BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; | 576 | BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; |
| 584 | ButtonsRaw debug_pad_buttons; | 577 | ButtonsRaw debug_pad_buttons; |
| @@ -586,14 +579,11 @@ struct Values { | |||
| 586 | 579 | ||
| 587 | TouchscreenInput touchscreen; | 580 | TouchscreenInput touchscreen; |
| 588 | 581 | ||
| 589 | BasicSetting<bool> use_touch_from_button{false, "use_touch_from_button"}; | ||
| 590 | BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", | 582 | BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", |
| 591 | "touch_device"}; | 583 | "touch_device"}; |
| 592 | BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; | 584 | BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; |
| 593 | std::vector<TouchFromButtonMap> touch_from_button_maps; | 585 | std::vector<TouchFromButtonMap> touch_from_button_maps; |
| 594 | 586 | ||
| 595 | std::atomic_bool is_device_reload_pending{true}; | ||
| 596 | |||
| 597 | // Data Storage | 587 | // Data Storage |
| 598 | BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; | 588 | BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; |
| 599 | BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; | 589 | BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; |
| @@ -614,6 +604,7 @@ struct Values { | |||
| 614 | BasicSetting<bool> extended_logging{false, "extended_logging"}; | 604 | BasicSetting<bool> extended_logging{false, "extended_logging"}; |
| 615 | BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; | 605 | BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; |
| 616 | BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; | 606 | BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; |
| 607 | BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"}; | ||
| 617 | 608 | ||
| 618 | // Miscellaneous | 609 | // Miscellaneous |
| 619 | BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; | 610 | BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; |
diff --git a/src/common/settings_input.h b/src/common/settings_input.h index 609600582..4ff37e186 100644 --- a/src/common/settings_input.h +++ b/src/common/settings_input.h | |||
| @@ -62,11 +62,22 @@ enum Values : int { | |||
| 62 | 62 | ||
| 63 | constexpr int STICK_HID_BEGIN = LStick; | 63 | constexpr int STICK_HID_BEGIN = LStick; |
| 64 | constexpr int STICK_HID_END = NumAnalogs; | 64 | constexpr int STICK_HID_END = NumAnalogs; |
| 65 | constexpr int NUM_STICKS_HID = NumAnalogs; | ||
| 66 | 65 | ||
| 67 | extern const std::array<const char*, NumAnalogs> mapping; | 66 | extern const std::array<const char*, NumAnalogs> mapping; |
| 68 | } // namespace NativeAnalog | 67 | } // namespace NativeAnalog |
| 69 | 68 | ||
| 69 | namespace NativeTrigger { | ||
| 70 | enum Values : int { | ||
| 71 | LTrigger, | ||
| 72 | RTrigger, | ||
| 73 | |||
| 74 | NumTriggers, | ||
| 75 | }; | ||
| 76 | |||
| 77 | constexpr int TRIGGER_HID_BEGIN = LTrigger; | ||
| 78 | constexpr int TRIGGER_HID_END = NumTriggers; | ||
| 79 | } // namespace NativeTrigger | ||
| 80 | |||
| 70 | namespace NativeVibration { | 81 | namespace NativeVibration { |
| 71 | enum Values : int { | 82 | enum Values : int { |
| 72 | LeftVibrationDevice, | 83 | LeftVibrationDevice, |
| @@ -115,10 +126,20 @@ constexpr int NUM_MOUSE_HID = NumMouseButtons; | |||
| 115 | extern const std::array<const char*, NumMouseButtons> mapping; | 126 | extern const std::array<const char*, NumMouseButtons> mapping; |
| 116 | } // namespace NativeMouseButton | 127 | } // namespace NativeMouseButton |
| 117 | 128 | ||
| 129 | namespace NativeMouseWheel { | ||
| 130 | enum Values { | ||
| 131 | X, | ||
| 132 | Y, | ||
| 133 | |||
| 134 | NumMouseWheels, | ||
| 135 | }; | ||
| 136 | |||
| 137 | extern const std::array<const char*, NumMouseWheels> mapping; | ||
| 138 | } // namespace NativeMouseWheel | ||
| 139 | |||
| 118 | namespace NativeKeyboard { | 140 | namespace NativeKeyboard { |
| 119 | enum Keys { | 141 | enum Keys { |
| 120 | None, | 142 | None, |
| 121 | Error, | ||
| 122 | 143 | ||
| 123 | A = 4, | 144 | A = 4, |
| 124 | B, | 145 | B, |
| @@ -156,22 +177,22 @@ enum Keys { | |||
| 156 | N8, | 177 | N8, |
| 157 | N9, | 178 | N9, |
| 158 | N0, | 179 | N0, |
| 159 | Enter, | 180 | Return, |
| 160 | Escape, | 181 | Escape, |
| 161 | Backspace, | 182 | Backspace, |
| 162 | Tab, | 183 | Tab, |
| 163 | Space, | 184 | Space, |
| 164 | Minus, | 185 | Minus, |
| 165 | Equal, | 186 | Plus, |
| 166 | LeftBrace, | 187 | OpenBracket, |
| 167 | RightBrace, | 188 | CloseBracket, |
| 168 | Backslash, | 189 | Pipe, |
| 169 | Tilde, | 190 | Tilde, |
| 170 | Semicolon, | 191 | Semicolon, |
| 171 | Apostrophe, | 192 | Quote, |
| 172 | Grave, | 193 | Backquote, |
| 173 | Comma, | 194 | Comma, |
| 174 | Dot, | 195 | Period, |
| 175 | Slash, | 196 | Slash, |
| 176 | CapsLockKey, | 197 | CapsLockKey, |
| 177 | 198 | ||
| @@ -188,7 +209,7 @@ enum Keys { | |||
| 188 | F11, | 209 | F11, |
| 189 | F12, | 210 | F12, |
| 190 | 211 | ||
| 191 | SystemRequest, | 212 | PrintScreen, |
| 192 | ScrollLockKey, | 213 | ScrollLockKey, |
| 193 | Pause, | 214 | Pause, |
| 194 | Insert, | 215 | Insert, |
| @@ -257,8 +278,18 @@ enum Keys { | |||
| 257 | ScrollLockActive, | 278 | ScrollLockActive, |
| 258 | KPComma, | 279 | KPComma, |
| 259 | 280 | ||
| 260 | KPLeftParenthesis, | 281 | Ro = 0x87, |
| 261 | KPRightParenthesis, | 282 | KatakanaHiragana, |
| 283 | Yen, | ||
| 284 | Henkan, | ||
| 285 | Muhenkan, | ||
| 286 | NumPadCommaPc98, | ||
| 287 | |||
| 288 | HangulEnglish = 0x90, | ||
| 289 | Hanja, | ||
| 290 | KatakanaKey, | ||
| 291 | HiraganaKey, | ||
| 292 | ZenkakuHankaku, | ||
| 262 | 293 | ||
| 263 | LeftControlKey = 0xE0, | 294 | LeftControlKey = 0xE0, |
| 264 | LeftShiftKey, | 295 | LeftShiftKey, |
| @@ -307,6 +338,8 @@ enum Modifiers { | |||
| 307 | CapsLock, | 338 | CapsLock, |
| 308 | ScrollLock, | 339 | ScrollLock, |
| 309 | NumLock, | 340 | NumLock, |
| 341 | Katakana, | ||
| 342 | Hiragana, | ||
| 310 | 343 | ||
| 311 | NumKeyboardMods, | 344 | NumKeyboardMods, |
| 312 | }; | 345 | }; |
| @@ -324,11 +357,6 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; | |||
| 324 | using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; | 357 | using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; |
| 325 | using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; | 358 | using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; |
| 326 | using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; | 359 | using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; |
| 327 | using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>; | ||
| 328 | |||
| 329 | using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; | ||
| 330 | using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; | ||
| 331 | using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; | ||
| 332 | 360 | ||
| 333 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; | 361 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; |
| 334 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; | 362 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; |
| @@ -342,6 +370,11 @@ enum class ControllerType { | |||
| 342 | RightJoycon, | 370 | RightJoycon, |
| 343 | Handheld, | 371 | Handheld, |
| 344 | GameCube, | 372 | GameCube, |
| 373 | Pokeball, | ||
| 374 | NES, | ||
| 375 | SNES, | ||
| 376 | N64, | ||
| 377 | SegaGenesis, | ||
| 345 | }; | 378 | }; |
| 346 | 379 | ||
| 347 | struct PlayerInput { | 380 | struct PlayerInput { |
| @@ -349,7 +382,6 @@ struct PlayerInput { | |||
| 349 | ControllerType controller_type; | 382 | ControllerType controller_type; |
| 350 | ButtonsRaw buttons; | 383 | ButtonsRaw buttons; |
| 351 | AnalogsRaw analogs; | 384 | AnalogsRaw analogs; |
| 352 | VibrationsRaw vibrations; | ||
| 353 | MotionsRaw motions; | 385 | MotionsRaw motions; |
| 354 | 386 | ||
| 355 | bool vibration_enabled; | 387 | bool vibration_enabled; |
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index fccd2eee5..fbeacc7e2 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp | |||
| @@ -71,9 +71,6 @@ static CPUCaps Detect() { | |||
| 71 | else | 71 | else |
| 72 | caps.manufacturer = Manufacturer::Unknown; | 72 | caps.manufacturer = Manufacturer::Unknown; |
| 73 | 73 | ||
| 74 | u32 family = {}; | ||
| 75 | u32 model = {}; | ||
| 76 | |||
| 77 | __cpuid(cpu_id, 0x80000000); | 74 | __cpuid(cpu_id, 0x80000000); |
| 78 | 75 | ||
| 79 | u32 max_ex_fn = cpu_id[0]; | 76 | u32 max_ex_fn = cpu_id[0]; |
| @@ -84,15 +81,6 @@ static CPUCaps Detect() { | |||
| 84 | // Detect family and other miscellaneous features | 81 | // Detect family and other miscellaneous features |
| 85 | if (max_std_fn >= 1) { | 82 | if (max_std_fn >= 1) { |
| 86 | __cpuid(cpu_id, 0x00000001); | 83 | __cpuid(cpu_id, 0x00000001); |
| 87 | family = (cpu_id[0] >> 8) & 0xf; | ||
| 88 | model = (cpu_id[0] >> 4) & 0xf; | ||
| 89 | if (family == 0xf) { | ||
| 90 | family += (cpu_id[0] >> 20) & 0xff; | ||
| 91 | } | ||
| 92 | if (family >= 6) { | ||
| 93 | model += ((cpu_id[0] >> 16) & 0xf) << 4; | ||
| 94 | } | ||
| 95 | |||
| 96 | if ((cpu_id[3] >> 25) & 1) | 84 | if ((cpu_id[3] >> 25) & 1) |
| 97 | caps.sse = true; | 85 | caps.sse = true; |
| 98 | if ((cpu_id[3] >> 26) & 1) | 86 | if ((cpu_id[3] >> 26) & 1) |
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 87de40624..82ee2c8a1 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp | |||
| @@ -15,26 +15,26 @@ | |||
| 15 | namespace Common { | 15 | namespace Common { |
| 16 | 16 | ||
| 17 | u64 EstimateRDTSCFrequency() { | 17 | u64 EstimateRDTSCFrequency() { |
| 18 | const auto milli_10 = std::chrono::milliseconds{10}; | 18 | // Discard the first result measuring the rdtsc. |
| 19 | // get current time | ||
| 20 | _mm_mfence(); | 19 | _mm_mfence(); |
| 21 | const u64 tscStart = __rdtsc(); | 20 | __rdtsc(); |
| 22 | const auto startTime = std::chrono::high_resolution_clock::now(); | 21 | std::this_thread::sleep_for(std::chrono::milliseconds{1}); |
| 23 | // wait roughly 3 seconds | 22 | _mm_mfence(); |
| 24 | while (true) { | 23 | __rdtsc(); |
| 25 | auto milli = std::chrono::duration_cast<std::chrono::milliseconds>( | 24 | |
| 26 | std::chrono::high_resolution_clock::now() - startTime); | 25 | // Get the current time. |
| 27 | if (milli.count() >= 3000) | 26 | const auto start_time = std::chrono::steady_clock::now(); |
| 28 | break; | 27 | _mm_mfence(); |
| 29 | std::this_thread::sleep_for(milli_10); | 28 | const u64 tsc_start = __rdtsc(); |
| 30 | } | 29 | // Wait for 200 milliseconds. |
| 31 | const auto endTime = std::chrono::high_resolution_clock::now(); | 30 | std::this_thread::sleep_for(std::chrono::milliseconds{200}); |
| 31 | const auto end_time = std::chrono::steady_clock::now(); | ||
| 32 | _mm_mfence(); | 32 | _mm_mfence(); |
| 33 | const u64 tscEnd = __rdtsc(); | 33 | const u64 tsc_end = __rdtsc(); |
| 34 | // calculate difference | 34 | // Calculate differences. |
| 35 | const u64 timer_diff = | 35 | const u64 timer_diff = static_cast<u64>( |
| 36 | std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count(); | 36 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); |
| 37 | const u64 tsc_diff = tscEnd - tscStart; | 37 | const u64 tsc_diff = tsc_end - tsc_start; |
| 38 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | 38 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); |
| 39 | return tsc_freq; | 39 | return tsc_freq; |
| 40 | } | 40 | } |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9f0fbba2d..49bed614a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -132,11 +132,23 @@ add_library(core STATIC | |||
| 132 | frontend/emu_window.h | 132 | frontend/emu_window.h |
| 133 | frontend/framebuffer_layout.cpp | 133 | frontend/framebuffer_layout.cpp |
| 134 | frontend/framebuffer_layout.h | 134 | frontend/framebuffer_layout.h |
| 135 | frontend/input_interpreter.cpp | ||
| 136 | frontend/input_interpreter.h | ||
| 137 | frontend/input.h | ||
| 138 | hardware_interrupt_manager.cpp | 135 | hardware_interrupt_manager.cpp |
| 139 | hardware_interrupt_manager.h | 136 | hardware_interrupt_manager.h |
| 137 | hid/emulated_console.cpp | ||
| 138 | hid/emulated_console.h | ||
| 139 | hid/emulated_controller.cpp | ||
| 140 | hid/emulated_controller.h | ||
| 141 | hid/emulated_devices.cpp | ||
| 142 | hid/emulated_devices.h | ||
| 143 | hid/hid_core.cpp | ||
| 144 | hid/hid_core.h | ||
| 145 | hid/hid_types.h | ||
| 146 | hid/input_converter.cpp | ||
| 147 | hid/input_converter.h | ||
| 148 | hid/input_interpreter.cpp | ||
| 149 | hid/input_interpreter.h | ||
| 150 | hid/motion_input.cpp | ||
| 151 | hid/motion_input.h | ||
| 140 | hle/api_version.h | 152 | hle/api_version.h |
| 141 | hle/ipc.h | 153 | hle/ipc.h |
| 142 | hle/ipc_helpers.h | 154 | hle/ipc_helpers.h |
| @@ -167,12 +179,15 @@ add_library(core STATIC | |||
| 167 | hle/kernel/k_client_port.h | 179 | hle/kernel/k_client_port.h |
| 168 | hle/kernel/k_client_session.cpp | 180 | hle/kernel/k_client_session.cpp |
| 169 | hle/kernel/k_client_session.h | 181 | hle/kernel/k_client_session.h |
| 182 | hle/kernel/k_code_memory.cpp | ||
| 183 | hle/kernel/k_code_memory.h | ||
| 170 | hle/kernel/k_condition_variable.cpp | 184 | hle/kernel/k_condition_variable.cpp |
| 171 | hle/kernel/k_condition_variable.h | 185 | hle/kernel/k_condition_variable.h |
| 172 | hle/kernel/k_event.cpp | 186 | hle/kernel/k_event.cpp |
| 173 | hle/kernel/k_event.h | 187 | hle/kernel/k_event.h |
| 174 | hle/kernel/k_handle_table.cpp | 188 | hle/kernel/k_handle_table.cpp |
| 175 | hle/kernel/k_handle_table.h | 189 | hle/kernel/k_handle_table.h |
| 190 | hle/kernel/k_light_condition_variable.cpp | ||
| 176 | hle/kernel/k_light_condition_variable.h | 191 | hle/kernel/k_light_condition_variable.h |
| 177 | hle/kernel/k_light_lock.cpp | 192 | hle/kernel/k_light_lock.cpp |
| 178 | hle/kernel/k_light_lock.h | 193 | hle/kernel/k_light_lock.h |
| @@ -225,6 +240,7 @@ add_library(core STATIC | |||
| 225 | hle/kernel/k_system_control.h | 240 | hle/kernel/k_system_control.h |
| 226 | hle/kernel/k_thread.cpp | 241 | hle/kernel/k_thread.cpp |
| 227 | hle/kernel/k_thread.h | 242 | hle/kernel/k_thread.h |
| 243 | hle/kernel/k_thread_queue.cpp | ||
| 228 | hle/kernel/k_thread_queue.h | 244 | hle/kernel/k_thread_queue.h |
| 229 | hle/kernel/k_trace.h | 245 | hle/kernel/k_trace.h |
| 230 | hle/kernel/k_transfer_memory.cpp | 246 | hle/kernel/k_transfer_memory.cpp |
| @@ -396,12 +412,15 @@ add_library(core STATIC | |||
| 396 | hle/service/glue/glue.h | 412 | hle/service/glue/glue.h |
| 397 | hle/service/glue/glue_manager.cpp | 413 | hle/service/glue/glue_manager.cpp |
| 398 | hle/service/glue/glue_manager.h | 414 | hle/service/glue/glue_manager.h |
| 415 | hle/service/glue/notif.cpp | ||
| 416 | hle/service/glue/notif.h | ||
| 399 | hle/service/grc/grc.cpp | 417 | hle/service/grc/grc.cpp |
| 400 | hle/service/grc/grc.h | 418 | hle/service/grc/grc.h |
| 401 | hle/service/hid/hid.cpp | 419 | hle/service/hid/hid.cpp |
| 402 | hle/service/hid/hid.h | 420 | hle/service/hid/hid.h |
| 403 | hle/service/hid/irs.cpp | 421 | hle/service/hid/irs.cpp |
| 404 | hle/service/hid/irs.h | 422 | hle/service/hid/irs.h |
| 423 | hle/service/hid/ring_lifo.h | ||
| 405 | hle/service/hid/xcd.cpp | 424 | hle/service/hid/xcd.cpp |
| 406 | hle/service/hid/xcd.h | 425 | hle/service/hid/xcd.h |
| 407 | hle/service/hid/errors.h | 426 | hle/service/hid/errors.h |
| @@ -466,6 +485,8 @@ add_library(core STATIC | |||
| 466 | hle/service/ns/language.h | 485 | hle/service/ns/language.h |
| 467 | hle/service/ns/ns.cpp | 486 | hle/service/ns/ns.cpp |
| 468 | hle/service/ns/ns.h | 487 | hle/service/ns/ns.h |
| 488 | hle/service/ns/pdm_qry.cpp | ||
| 489 | hle/service/ns/pdm_qry.h | ||
| 469 | hle/service/ns/pl_u.cpp | 490 | hle/service/ns/pl_u.cpp |
| 470 | hle/service/ns/pl_u.h | 491 | hle/service/ns/pl_u.h |
| 471 | hle/service/nvdrv/devices/nvdevice.h | 492 | hle/service/nvdrv/devices/nvdevice.h |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 4e73cc03a..56836bd05 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -86,6 +86,26 @@ public: | |||
| 86 | num_instructions, MemoryReadCode(pc)); | 86 | num_instructions, MemoryReadCode(pc)); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, | ||
| 90 | VAddr value) override { | ||
| 91 | switch (op) { | ||
| 92 | case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: { | ||
| 93 | static constexpr u64 ICACHE_LINE_SIZE = 64; | ||
| 94 | |||
| 95 | const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); | ||
| 96 | parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); | ||
| 97 | break; | ||
| 98 | } | ||
| 99 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: | ||
| 100 | parent.ClearInstructionCache(); | ||
| 101 | break; | ||
| 102 | case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: | ||
| 103 | default: | ||
| 104 | LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 89 | void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { | 109 | void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { |
| 90 | switch (exception) { | 110 | switch (exception) { |
| 91 | case Dynarmic::A64::Exception::WaitForInterrupt: | 111 | case Dynarmic::A64::Exception::WaitForInterrupt: |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 07448fd29..aa96f709b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include "core/file_sys/vfs_concat.h" | 27 | #include "core/file_sys/vfs_concat.h" |
| 28 | #include "core/file_sys/vfs_real.h" | 28 | #include "core/file_sys/vfs_real.h" |
| 29 | #include "core/hardware_interrupt_manager.h" | 29 | #include "core/hardware_interrupt_manager.h" |
| 30 | #include "core/hid/hid_core.h" | ||
| 30 | #include "core/hle/kernel/k_process.h" | 31 | #include "core/hle/kernel/k_process.h" |
| 31 | #include "core/hle/kernel/k_scheduler.h" | 32 | #include "core/hle/kernel/k_scheduler.h" |
| 32 | #include "core/hle/kernel/kernel.h" | 33 | #include "core/hle/kernel/kernel.h" |
| @@ -126,7 +127,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 126 | 127 | ||
| 127 | struct System::Impl { | 128 | struct System::Impl { |
| 128 | explicit Impl(System& system) | 129 | explicit Impl(System& system) |
| 129 | : kernel{system}, fs_controller{system}, memory{system}, | 130 | : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, |
| 130 | cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} | 131 | cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} |
| 131 | 132 | ||
| 132 | SystemResultStatus Run() { | 133 | SystemResultStatus Run() { |
| @@ -391,6 +392,7 @@ struct System::Impl { | |||
| 391 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | 392 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; |
| 392 | std::unique_ptr<Core::DeviceMemory> device_memory; | 393 | std::unique_ptr<Core::DeviceMemory> device_memory; |
| 393 | Core::Memory::Memory memory; | 394 | Core::Memory::Memory memory; |
| 395 | Core::HID::HIDCore hid_core; | ||
| 394 | CpuManager cpu_manager; | 396 | CpuManager cpu_manager; |
| 395 | std::atomic_bool is_powered_on{}; | 397 | std::atomic_bool is_powered_on{}; |
| 396 | bool exit_lock = false; | 398 | bool exit_lock = false; |
| @@ -519,12 +521,6 @@ const ARM_Interface& System::CurrentArmInterface() const { | |||
| 519 | return impl->kernel.CurrentPhysicalCore().ArmInterface(); | 521 | return impl->kernel.CurrentPhysicalCore().ArmInterface(); |
| 520 | } | 522 | } |
| 521 | 523 | ||
| 522 | std::size_t System::CurrentCoreIndex() const { | ||
| 523 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 524 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | ||
| 525 | return core; | ||
| 526 | } | ||
| 527 | |||
| 528 | Kernel::PhysicalCore& System::CurrentPhysicalCore() { | 524 | Kernel::PhysicalCore& System::CurrentPhysicalCore() { |
| 529 | return impl->kernel.CurrentPhysicalCore(); | 525 | return impl->kernel.CurrentPhysicalCore(); |
| 530 | } | 526 | } |
| @@ -615,6 +611,14 @@ const Kernel::KernelCore& System::Kernel() const { | |||
| 615 | return impl->kernel; | 611 | return impl->kernel; |
| 616 | } | 612 | } |
| 617 | 613 | ||
| 614 | HID::HIDCore& System::HIDCore() { | ||
| 615 | return impl->hid_core; | ||
| 616 | } | ||
| 617 | |||
| 618 | const HID::HIDCore& System::HIDCore() const { | ||
| 619 | return impl->hid_core; | ||
| 620 | } | ||
| 621 | |||
| 618 | Timing::CoreTiming& System::CoreTiming() { | 622 | Timing::CoreTiming& System::CoreTiming() { |
| 619 | return impl->core_timing; | 623 | return impl->core_timing; |
| 620 | } | 624 | } |
| @@ -825,8 +829,6 @@ void System::ApplySettings() { | |||
| 825 | if (IsPoweredOn()) { | 829 | if (IsPoweredOn()) { |
| 826 | Renderer().RefreshBaseSettings(); | 830 | Renderer().RefreshBaseSettings(); |
| 827 | } | 831 | } |
| 828 | |||
| 829 | Service::HID::ReloadInputDevices(); | ||
| 830 | } | 832 | } |
| 831 | 833 | ||
| 832 | } // namespace Core | 834 | } // namespace Core |
diff --git a/src/core/core.h b/src/core/core.h index 01bc0a2c7..52ff90359 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -89,6 +89,10 @@ namespace Core::Hardware { | |||
| 89 | class InterruptManager; | 89 | class InterruptManager; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | namespace Core::HID { | ||
| 93 | class HIDCore; | ||
| 94 | } | ||
| 95 | |||
| 92 | namespace Core { | 96 | namespace Core { |
| 93 | 97 | ||
| 94 | class ARM_Interface; | 98 | class ARM_Interface; |
| @@ -204,9 +208,6 @@ public: | |||
| 204 | /// Gets an ARM interface to the CPU core that is currently running | 208 | /// Gets an ARM interface to the CPU core that is currently running |
| 205 | [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; | 209 | [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; |
| 206 | 210 | ||
| 207 | /// Gets the index of the currently running CPU core | ||
| 208 | [[nodiscard]] std::size_t CurrentCoreIndex() const; | ||
| 209 | |||
| 210 | /// Gets the physical core for the CPU core that is currently running | 211 | /// Gets the physical core for the CPU core that is currently running |
| 211 | [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); | 212 | [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); |
| 212 | 213 | ||
| @@ -285,6 +286,12 @@ public: | |||
| 285 | /// Provides a constant reference to the kernel instance. | 286 | /// Provides a constant reference to the kernel instance. |
| 286 | [[nodiscard]] const Kernel::KernelCore& Kernel() const; | 287 | [[nodiscard]] const Kernel::KernelCore& Kernel() const; |
| 287 | 288 | ||
| 289 | /// Gets a mutable reference to the HID interface. | ||
| 290 | [[nodiscard]] HID::HIDCore& HIDCore(); | ||
| 291 | |||
| 292 | /// Gets an immutable reference to the HID interface. | ||
| 293 | [[nodiscard]] const HID::HIDCore& HIDCore() const; | ||
| 294 | |||
| 288 | /// Provides a reference to the internal PerfStats instance. | 295 | /// Provides a reference to the internal PerfStats instance. |
| 289 | [[nodiscard]] Core::PerfStats& GetPerfStats(); | 296 | [[nodiscard]] Core::PerfStats& GetPerfStats(); |
| 290 | 297 | ||
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 5d43c6e5d..cbcc54891 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -117,17 +117,18 @@ void CpuManager::MultiCoreRunGuestLoop() { | |||
| 117 | physical_core = &kernel.CurrentPhysicalCore(); | 117 | physical_core = &kernel.CurrentPhysicalCore(); |
| 118 | } | 118 | } |
| 119 | system.ExitDynarmicProfile(); | 119 | system.ExitDynarmicProfile(); |
| 120 | physical_core->ArmInterface().ClearExclusiveState(); | 120 | { |
| 121 | kernel.CurrentScheduler()->RescheduleCurrentCore(); | 121 | Kernel::KScopedDisableDispatch dd(kernel); |
| 122 | physical_core->ArmInterface().ClearExclusiveState(); | ||
| 123 | } | ||
| 122 | } | 124 | } |
| 123 | } | 125 | } |
| 124 | 126 | ||
| 125 | void CpuManager::MultiCoreRunIdleThread() { | 127 | void CpuManager::MultiCoreRunIdleThread() { |
| 126 | auto& kernel = system.Kernel(); | 128 | auto& kernel = system.Kernel(); |
| 127 | while (true) { | 129 | while (true) { |
| 128 | auto& physical_core = kernel.CurrentPhysicalCore(); | 130 | Kernel::KScopedDisableDispatch dd(kernel); |
| 129 | physical_core.Idle(); | 131 | kernel.CurrentPhysicalCore().Idle(); |
| 130 | kernel.CurrentScheduler()->RescheduleCurrentCore(); | ||
| 131 | } | 132 | } |
| 132 | } | 133 | } |
| 133 | 134 | ||
| @@ -135,12 +136,12 @@ void CpuManager::MultiCoreRunSuspendThread() { | |||
| 135 | auto& kernel = system.Kernel(); | 136 | auto& kernel = system.Kernel(); |
| 136 | kernel.CurrentScheduler()->OnThreadStart(); | 137 | kernel.CurrentScheduler()->OnThreadStart(); |
| 137 | while (true) { | 138 | while (true) { |
| 138 | auto core = kernel.GetCurrentHostThreadID(); | 139 | auto core = kernel.CurrentPhysicalCoreIndex(); |
| 139 | auto& scheduler = *kernel.CurrentScheduler(); | 140 | auto& scheduler = *kernel.CurrentScheduler(); |
| 140 | Kernel::KThread* current_thread = scheduler.GetCurrentThread(); | 141 | Kernel::KThread* current_thread = scheduler.GetCurrentThread(); |
| 141 | Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); | 142 | Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); |
| 142 | ASSERT(scheduler.ContextSwitchPending()); | 143 | ASSERT(scheduler.ContextSwitchPending()); |
| 143 | ASSERT(core == kernel.GetCurrentHostThreadID()); | 144 | ASSERT(core == kernel.CurrentPhysicalCoreIndex()); |
| 144 | scheduler.RescheduleCurrentCore(); | 145 | scheduler.RescheduleCurrentCore(); |
| 145 | } | 146 | } |
| 146 | } | 147 | } |
| @@ -346,13 +347,9 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { | |||
| 346 | sc_sync_first_use = false; | 347 | sc_sync_first_use = false; |
| 347 | } | 348 | } |
| 348 | 349 | ||
| 349 | // Abort if emulation was killed before the session really starts | 350 | // Emulation was stopped |
| 350 | if (!system.IsPoweredOn()) { | ||
| 351 | return; | ||
| 352 | } | ||
| 353 | |||
| 354 | if (stop_token.stop_requested()) { | 351 | if (stop_token.stop_requested()) { |
| 355 | break; | 352 | return; |
| 356 | } | 353 | } |
| 357 | 354 | ||
| 358 | auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); | 355 | auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); |
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 03bbedf8b..6dbd38ffa 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp | |||
| @@ -5,16 +5,15 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/frontend/applets/controller.h" | 7 | #include "core/frontend/applets/controller.h" |
| 8 | #include "core/hle/service/hid/controllers/npad.h" | 8 | #include "core/hid/emulated_controller.h" |
| 9 | #include "core/hle/service/hid/hid.h" | 9 | #include "core/hid/hid_core.h" |
| 10 | #include "core/hle/service/sm/sm.h" | 10 | #include "core/hid/hid_types.h" |
| 11 | 11 | ||
| 12 | namespace Core::Frontend { | 12 | namespace Core::Frontend { |
| 13 | 13 | ||
| 14 | ControllerApplet::~ControllerApplet() = default; | 14 | ControllerApplet::~ControllerApplet() = default; |
| 15 | 15 | ||
| 16 | DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_) | 16 | DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_core{hid_core_} {} |
| 17 | : service_manager{service_manager_} {} | ||
| 18 | 17 | ||
| 19 | DefaultControllerApplet::~DefaultControllerApplet() = default; | 18 | DefaultControllerApplet::~DefaultControllerApplet() = default; |
| 20 | 19 | ||
| @@ -22,24 +21,20 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb | |||
| 22 | const ControllerParameters& parameters) const { | 21 | const ControllerParameters& parameters) const { |
| 23 | LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); | 22 | LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); |
| 24 | 23 | ||
| 25 | auto& npad = | ||
| 26 | service_manager.GetService<Service::HID::Hid>("hid") | ||
| 27 | ->GetAppletResource() | ||
| 28 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); | ||
| 29 | |||
| 30 | auto& players = Settings::values.players.GetValue(); | ||
| 31 | |||
| 32 | const std::size_t min_supported_players = | 24 | const std::size_t min_supported_players = |
| 33 | parameters.enable_single_mode ? 1 : parameters.min_players; | 25 | parameters.enable_single_mode ? 1 : parameters.min_players; |
| 34 | 26 | ||
| 35 | // Disconnect Handheld first. | 27 | // Disconnect Handheld first. |
| 36 | npad.DisconnectNpadAtIndex(8); | 28 | auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 29 | handheld->Disconnect(); | ||
| 37 | 30 | ||
| 38 | // Deduce the best configuration based on the input parameters. | 31 | // Deduce the best configuration based on the input parameters. |
| 39 | for (std::size_t index = 0; index < players.size() - 2; ++index) { | 32 | for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) { |
| 33 | auto* controller = hid_core.GetEmulatedControllerByIndex(index); | ||
| 34 | |||
| 40 | // First, disconnect all controllers regardless of the value of keep_controllers_connected. | 35 | // First, disconnect all controllers regardless of the value of keep_controllers_connected. |
| 41 | // This makes it easy to connect the desired controllers. | 36 | // This makes it easy to connect the desired controllers. |
| 42 | npad.DisconnectNpadAtIndex(index); | 37 | controller->Disconnect(); |
| 43 | 38 | ||
| 44 | // Only connect the minimum number of required players. | 39 | // Only connect the minimum number of required players. |
| 45 | if (index >= min_supported_players) { | 40 | if (index >= min_supported_players) { |
| @@ -49,27 +44,27 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb | |||
| 49 | // Connect controllers based on the following priority list from highest to lowest priority: | 44 | // Connect controllers based on the following priority list from highest to lowest priority: |
| 50 | // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld | 45 | // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld |
| 51 | if (parameters.allow_pro_controller) { | 46 | if (parameters.allow_pro_controller) { |
| 52 | npad.AddNewControllerAt( | 47 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); |
| 53 | npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index); | 48 | controller->Connect(); |
| 54 | } else if (parameters.allow_dual_joycons) { | 49 | } else if (parameters.allow_dual_joycons) { |
| 55 | npad.AddNewControllerAt( | 50 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual); |
| 56 | npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index); | 51 | controller->Connect(); |
| 57 | } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) { | 52 | } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) { |
| 58 | // Assign left joycons to even player indices and right joycons to odd player indices. | 53 | // Assign left joycons to even player indices and right joycons to odd player indices. |
| 59 | // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and | 54 | // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and |
| 60 | // a right Joycon for Player 2 in 2 Player Assist mode. | 55 | // a right Joycon for Player 2 in 2 Player Assist mode. |
| 61 | if (index % 2 == 0) { | 56 | if (index % 2 == 0) { |
| 62 | npad.AddNewControllerAt( | 57 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft); |
| 63 | npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index); | 58 | controller->Connect(); |
| 64 | } else { | 59 | } else { |
| 65 | npad.AddNewControllerAt( | 60 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight); |
| 66 | npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); | 61 | controller->Connect(); |
| 67 | } | 62 | } |
| 68 | } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && | 63 | } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && |
| 69 | !Settings::values.use_docked_mode.GetValue()) { | 64 | !Settings::values.use_docked_mode.GetValue()) { |
| 70 | // We should *never* reach here under any normal circumstances. | 65 | // We should *never* reach here under any normal circumstances. |
| 71 | npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), | 66 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); |
| 72 | index); | 67 | controller->Connect(); |
| 73 | } else { | 68 | } else { |
| 74 | UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); | 69 | UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); |
| 75 | } | 70 | } |
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h index b0626a0f9..014bc8901 100644 --- a/src/core/frontend/applets/controller.h +++ b/src/core/frontend/applets/controller.h | |||
| @@ -8,8 +8,8 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | 10 | ||
| 11 | namespace Service::SM { | 11 | namespace Core::HID { |
| 12 | class ServiceManager; | 12 | class HIDCore; |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | namespace Core::Frontend { | 15 | namespace Core::Frontend { |
| @@ -44,14 +44,14 @@ public: | |||
| 44 | 44 | ||
| 45 | class DefaultControllerApplet final : public ControllerApplet { | 45 | class DefaultControllerApplet final : public ControllerApplet { |
| 46 | public: | 46 | public: |
| 47 | explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_); | 47 | explicit DefaultControllerApplet(HID::HIDCore& hid_core_); |
| 48 | ~DefaultControllerApplet() override; | 48 | ~DefaultControllerApplet() override; |
| 49 | 49 | ||
| 50 | void ReconfigureControllers(std::function<void()> callback, | 50 | void ReconfigureControllers(std::function<void()> callback, |
| 51 | const ControllerParameters& parameters) const override; | 51 | const ControllerParameters& parameters) const override; |
| 52 | 52 | ||
| 53 | private: | 53 | private: |
| 54 | Service::SM::ServiceManager& service_manager; | 54 | HID::HIDCore& hid_core; |
| 55 | }; | 55 | }; |
| 56 | 56 | ||
| 57 | } // namespace Core::Frontend | 57 | } // namespace Core::Frontend |
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index e1f7e5886..57c6ffc43 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp | |||
| @@ -3,87 +3,23 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <mutex> | 5 | #include <mutex> |
| 6 | #include "common/settings.h" | ||
| 7 | #include "core/frontend/emu_window.h" | 6 | #include "core/frontend/emu_window.h" |
| 8 | #include "core/frontend/input.h" | ||
| 9 | 7 | ||
| 10 | namespace Core::Frontend { | 8 | namespace Core::Frontend { |
| 11 | 9 | ||
| 12 | GraphicsContext::~GraphicsContext() = default; | 10 | GraphicsContext::~GraphicsContext() = default; |
| 13 | 11 | ||
| 14 | class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>, | ||
| 15 | public std::enable_shared_from_this<TouchState> { | ||
| 16 | public: | ||
| 17 | std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override { | ||
| 18 | return std::make_unique<Device>(shared_from_this()); | ||
| 19 | } | ||
| 20 | |||
| 21 | std::mutex mutex; | ||
| 22 | |||
| 23 | Input::TouchStatus status; | ||
| 24 | |||
| 25 | private: | ||
| 26 | class Device : public Input::TouchDevice { | ||
| 27 | public: | ||
| 28 | explicit Device(std::weak_ptr<TouchState>&& touch_state_) : touch_state(touch_state_) {} | ||
| 29 | Input::TouchStatus GetStatus() const override { | ||
| 30 | if (auto state = touch_state.lock()) { | ||
| 31 | std::lock_guard guard{state->mutex}; | ||
| 32 | return state->status; | ||
| 33 | } | ||
| 34 | return {}; | ||
| 35 | } | ||
| 36 | |||
| 37 | private: | ||
| 38 | std::weak_ptr<TouchState> touch_state; | ||
| 39 | }; | ||
| 40 | }; | ||
| 41 | |||
| 42 | EmuWindow::EmuWindow() { | 12 | EmuWindow::EmuWindow() { |
| 43 | // TODO: Find a better place to set this. | 13 | // TODO: Find a better place to set this. |
| 44 | config.min_client_area_size = | 14 | config.min_client_area_size = |
| 45 | std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height); | 15 | std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height); |
| 46 | active_config = config; | 16 | active_config = config; |
| 47 | touch_state = std::make_shared<TouchState>(); | ||
| 48 | Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state); | ||
| 49 | } | ||
| 50 | |||
| 51 | EmuWindow::~EmuWindow() { | ||
| 52 | Input::UnregisterFactory<Input::TouchDevice>("emu_window"); | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout | ||
| 57 | * @param layout FramebufferLayout object describing the framebuffer size and screen positions | ||
| 58 | * @param framebuffer_x Framebuffer x-coordinate to check | ||
| 59 | * @param framebuffer_y Framebuffer y-coordinate to check | ||
| 60 | * @return True if the coordinates are within the touchpad, otherwise false | ||
| 61 | */ | ||
| 62 | static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, u32 framebuffer_x, | ||
| 63 | u32 framebuffer_y) { | ||
| 64 | return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom && | ||
| 65 | framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right); | ||
| 66 | } | ||
| 67 | |||
| 68 | std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const { | ||
| 69 | new_x = std::max(new_x, framebuffer_layout.screen.left); | ||
| 70 | new_x = std::min(new_x, framebuffer_layout.screen.right - 1); | ||
| 71 | |||
| 72 | new_y = std::max(new_y, framebuffer_layout.screen.top); | ||
| 73 | new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1); | ||
| 74 | |||
| 75 | return std::make_pair(new_x, new_y); | ||
| 76 | } | 17 | } |
| 77 | 18 | ||
| 78 | void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) { | 19 | EmuWindow::~EmuWindow() {} |
| 79 | if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { | ||
| 80 | return; | ||
| 81 | } | ||
| 82 | if (id >= touch_state->status.size()) { | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | 20 | ||
| 86 | std::lock_guard guard{touch_state->mutex}; | 21 | std::pair<f32, f32> EmuWindow::MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const { |
| 22 | std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); | ||
| 87 | const float x = | 23 | const float x = |
| 88 | static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / | 24 | static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / |
| 89 | static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); | 25 | static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); |
| @@ -91,31 +27,17 @@ void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) { | |||
| 91 | static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / | 27 | static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / |
| 92 | static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); | 28 | static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); |
| 93 | 29 | ||
| 94 | touch_state->status[id] = std::make_tuple(x, y, true); | 30 | return std::make_pair(x, y); |
| 95 | } | ||
| 96 | |||
| 97 | void EmuWindow::TouchReleased(size_t id) { | ||
| 98 | if (id >= touch_state->status.size()) { | ||
| 99 | return; | ||
| 100 | } | ||
| 101 | std::lock_guard guard{touch_state->mutex}; | ||
| 102 | touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false); | ||
| 103 | } | 31 | } |
| 104 | 32 | ||
| 105 | void EmuWindow::TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id) { | 33 | std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const { |
| 106 | if (id >= touch_state->status.size()) { | 34 | new_x = std::max(new_x, framebuffer_layout.screen.left); |
| 107 | return; | 35 | new_x = std::min(new_x, framebuffer_layout.screen.right - 1); |
| 108 | } | ||
| 109 | |||
| 110 | if (!std::get<2>(touch_state->status[id])) { | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | 36 | ||
| 114 | if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { | 37 | new_y = std::max(new_y, framebuffer_layout.screen.top); |
| 115 | std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); | 38 | new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1); |
| 116 | } | ||
| 117 | 39 | ||
| 118 | TouchPressed(framebuffer_x, framebuffer_y, id); | 40 | return std::make_pair(new_x, new_y); |
| 119 | } | 41 | } |
| 120 | 42 | ||
| 121 | void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) { | 43 | void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) { |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 8a86a1d27..e413a520a 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -113,28 +113,6 @@ public: | |||
| 113 | virtual bool IsShown() const = 0; | 113 | virtual bool IsShown() const = 0; |
| 114 | 114 | ||
| 115 | /** | 115 | /** |
| 116 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | ||
| 117 | * @param framebuffer_x Framebuffer x-coordinate that was pressed | ||
| 118 | * @param framebuffer_y Framebuffer y-coordinate that was pressed | ||
| 119 | * @param id Touch event ID | ||
| 120 | */ | ||
| 121 | void TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id); | ||
| 122 | |||
| 123 | /** | ||
| 124 | * Signal that a touch released event has occurred (e.g. mouse click released) | ||
| 125 | * @param id Touch event ID | ||
| 126 | */ | ||
| 127 | void TouchReleased(size_t id); | ||
| 128 | |||
| 129 | /** | ||
| 130 | * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window) | ||
| 131 | * @param framebuffer_x Framebuffer x-coordinate | ||
| 132 | * @param framebuffer_y Framebuffer y-coordinate | ||
| 133 | * @param id Touch event ID | ||
| 134 | */ | ||
| 135 | void TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id); | ||
| 136 | |||
| 137 | /** | ||
| 138 | * Returns currently active configuration. | 116 | * Returns currently active configuration. |
| 139 | * @note Accesses to the returned object need not be consistent because it may be modified in | 117 | * @note Accesses to the returned object need not be consistent because it may be modified in |
| 140 | * another thread | 118 | * another thread |
| @@ -212,6 +190,11 @@ protected: | |||
| 212 | client_area_height = size.second; | 190 | client_area_height = size.second; |
| 213 | } | 191 | } |
| 214 | 192 | ||
| 193 | /** | ||
| 194 | * Converts a screen postion into the equivalent touchscreen position. | ||
| 195 | */ | ||
| 196 | std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const; | ||
| 197 | |||
| 215 | WindowSystemInfo window_info; | 198 | WindowSystemInfo window_info; |
| 216 | 199 | ||
| 217 | private: | 200 | private: |
| @@ -237,9 +220,6 @@ private: | |||
| 237 | WindowConfig config; ///< Internal configuration (changes pending for being applied in | 220 | WindowConfig config; ///< Internal configuration (changes pending for being applied in |
| 238 | /// ProcessConfigurationChanges) | 221 | /// ProcessConfigurationChanges) |
| 239 | WindowConfig active_config; ///< Internal active configuration | 222 | WindowConfig active_config; ///< Internal active configuration |
| 240 | |||
| 241 | class TouchState; | ||
| 242 | std::shared_ptr<TouchState> touch_state; | ||
| 243 | }; | 223 | }; |
| 244 | 224 | ||
| 245 | } // namespace Core::Frontend | 225 | } // namespace Core::Frontend |
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 4b58b672a..26a5b12aa 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp | |||
| @@ -25,7 +25,12 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { | |||
| 25 | ASSERT(height > 0); | 25 | ASSERT(height > 0); |
| 26 | // The drawing code needs at least somewhat valid values for both screens | 26 | // The drawing code needs at least somewhat valid values for both screens |
| 27 | // so just calculate them both even if the other isn't showing. | 27 | // so just calculate them both even if the other isn't showing. |
| 28 | FramebufferLayout res{width, height, false, {}}; | 28 | FramebufferLayout res{ |
| 29 | .width = width, | ||
| 30 | .height = height, | ||
| 31 | .screen = {}, | ||
| 32 | .is_srgb = false, | ||
| 33 | }; | ||
| 29 | 34 | ||
| 30 | const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width); | 35 | const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width); |
| 31 | const float emulation_aspect_ratio = EmulationAspectRatio( | 36 | const float emulation_aspect_ratio = EmulationAspectRatio( |
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 2e36c0163..8e341e4e2 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h | |||
| @@ -35,17 +35,8 @@ enum class AspectRatio { | |||
| 35 | struct FramebufferLayout { | 35 | struct FramebufferLayout { |
| 36 | u32 width{ScreenUndocked::Width}; | 36 | u32 width{ScreenUndocked::Width}; |
| 37 | u32 height{ScreenUndocked::Height}; | 37 | u32 height{ScreenUndocked::Height}; |
| 38 | bool is_srgb{}; | ||
| 39 | |||
| 40 | Common::Rectangle<u32> screen; | 38 | Common::Rectangle<u32> screen; |
| 41 | 39 | bool is_srgb{}; | |
| 42 | /** | ||
| 43 | * Returns the ration of pixel size of the screen, compared to the native size of the undocked | ||
| 44 | * Switch screen. | ||
| 45 | */ | ||
| 46 | float GetScalingRatio() const { | ||
| 47 | return static_cast<float>(screen.GetWidth()) / ScreenUndocked::Width; | ||
| 48 | } | ||
| 49 | }; | 40 | }; |
| 50 | 41 | ||
| 51 | /** | 42 | /** |
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h deleted file mode 100644 index f1747c5b2..000000000 --- a/src/core/frontend/input.h +++ /dev/null | |||
| @@ -1,217 +0,0 @@ | |||
| 1 | // Copyright 2017 Citra 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 <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <tuple> | ||
| 11 | #include <unordered_map> | ||
| 12 | #include <utility> | ||
| 13 | #include "common/logging/log.h" | ||
| 14 | #include "common/param_package.h" | ||
| 15 | #include "common/quaternion.h" | ||
| 16 | #include "common/vector_math.h" | ||
| 17 | |||
| 18 | namespace Input { | ||
| 19 | |||
| 20 | enum class AnalogDirection : u8 { | ||
| 21 | RIGHT, | ||
| 22 | LEFT, | ||
| 23 | UP, | ||
| 24 | DOWN, | ||
| 25 | }; | ||
| 26 | struct AnalogProperties { | ||
| 27 | float deadzone; | ||
| 28 | float range; | ||
| 29 | float threshold; | ||
| 30 | }; | ||
| 31 | template <typename StatusType> | ||
| 32 | struct InputCallback { | ||
| 33 | std::function<void(StatusType)> on_change; | ||
| 34 | }; | ||
| 35 | |||
| 36 | /// An abstract class template for an input device (a button, an analog input, etc.). | ||
| 37 | template <typename StatusType> | ||
| 38 | class InputDevice { | ||
| 39 | public: | ||
| 40 | virtual ~InputDevice() = default; | ||
| 41 | virtual StatusType GetStatus() const { | ||
| 42 | return {}; | ||
| 43 | } | ||
| 44 | virtual StatusType GetRawStatus() const { | ||
| 45 | return GetStatus(); | ||
| 46 | } | ||
| 47 | virtual AnalogProperties GetAnalogProperties() const { | ||
| 48 | return {}; | ||
| 49 | } | ||
| 50 | virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const { | ||
| 51 | return {}; | ||
| 52 | } | ||
| 53 | virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low, | ||
| 54 | [[maybe_unused]] f32 amp_high, | ||
| 55 | [[maybe_unused]] f32 freq_high) const { | ||
| 56 | return {}; | ||
| 57 | } | ||
| 58 | void SetCallback(InputCallback<StatusType> callback_) { | ||
| 59 | callback = std::move(callback_); | ||
| 60 | } | ||
| 61 | void TriggerOnChange() { | ||
| 62 | if (callback.on_change) { | ||
| 63 | callback.on_change(GetStatus()); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | private: | ||
| 68 | InputCallback<StatusType> callback; | ||
| 69 | }; | ||
| 70 | |||
| 71 | /// An abstract class template for a factory that can create input devices. | ||
| 72 | template <typename InputDeviceType> | ||
| 73 | class Factory { | ||
| 74 | public: | ||
| 75 | virtual ~Factory() = default; | ||
| 76 | virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0; | ||
| 77 | }; | ||
| 78 | |||
| 79 | namespace Impl { | ||
| 80 | |||
| 81 | template <typename InputDeviceType> | ||
| 82 | using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>; | ||
| 83 | |||
| 84 | template <typename InputDeviceType> | ||
| 85 | struct FactoryList { | ||
| 86 | static FactoryListType<InputDeviceType> list; | ||
| 87 | }; | ||
| 88 | |||
| 89 | template <typename InputDeviceType> | ||
| 90 | FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list; | ||
| 91 | |||
| 92 | } // namespace Impl | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Registers an input device factory. | ||
| 96 | * @tparam InputDeviceType the type of input devices the factory can create | ||
| 97 | * @param name the name of the factory. Will be used to match the "engine" parameter when creating | ||
| 98 | * a device | ||
| 99 | * @param factory the factory object to register | ||
| 100 | */ | ||
| 101 | template <typename InputDeviceType> | ||
| 102 | void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) { | ||
| 103 | auto pair = std::make_pair(name, std::move(factory)); | ||
| 104 | if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) { | ||
| 105 | LOG_ERROR(Input, "Factory '{}' already registered", name); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Unregisters an input device factory. | ||
| 111 | * @tparam InputDeviceType the type of input devices the factory can create | ||
| 112 | * @param name the name of the factory to unregister | ||
| 113 | */ | ||
| 114 | template <typename InputDeviceType> | ||
| 115 | void UnregisterFactory(const std::string& name) { | ||
| 116 | if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) { | ||
| 117 | LOG_ERROR(Input, "Factory '{}' not registered", name); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Create an input device from given paramters. | ||
| 123 | * @tparam InputDeviceType the type of input devices to create | ||
| 124 | * @param params a serialized ParamPackage string contains all parameters for creating the device | ||
| 125 | */ | ||
| 126 | template <typename InputDeviceType> | ||
| 127 | std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) { | ||
| 128 | const Common::ParamPackage package(params); | ||
| 129 | const std::string engine = package.Get("engine", "null"); | ||
| 130 | const auto& factory_list = Impl::FactoryList<InputDeviceType>::list; | ||
| 131 | const auto pair = factory_list.find(engine); | ||
| 132 | if (pair == factory_list.end()) { | ||
| 133 | if (engine != "null") { | ||
| 134 | LOG_ERROR(Input, "Unknown engine name: {}", engine); | ||
| 135 | } | ||
| 136 | return std::make_unique<InputDeviceType>(); | ||
| 137 | } | ||
| 138 | return pair->second->Create(package); | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * A button device is an input device that returns bool as status. | ||
| 143 | * true for pressed; false for released. | ||
| 144 | */ | ||
| 145 | using ButtonDevice = InputDevice<bool>; | ||
| 146 | |||
| 147 | /** | ||
| 148 | * An analog device is an input device that returns a tuple of x and y coordinates as status. The | ||
| 149 | * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up | ||
| 150 | * direction | ||
| 151 | */ | ||
| 152 | using AnalogDevice = InputDevice<std::tuple<float, float>>; | ||
| 153 | |||
| 154 | /** | ||
| 155 | * A vibration device is an input device that returns an unsigned byte as status. | ||
| 156 | * It represents whether the vibration device supports vibration or not. | ||
| 157 | * If the status returns 1, it supports vibration. Otherwise, it does not support vibration. | ||
| 158 | */ | ||
| 159 | using VibrationDevice = InputDevice<u8>; | ||
| 160 | |||
| 161 | /** | ||
| 162 | * A motion status is an object that returns a tuple of accelerometer state vector, | ||
| 163 | * gyroscope state vector, rotation state vector, orientation state matrix and quaterion state | ||
| 164 | * vector. | ||
| 165 | * | ||
| 166 | * For both 3D vectors: | ||
| 167 | * x+ is the same direction as RIGHT on D-pad. | ||
| 168 | * y+ is normal to the touch screen, pointing outward. | ||
| 169 | * z+ is the same direction as UP on D-pad. | ||
| 170 | * | ||
| 171 | * For accelerometer state vector | ||
| 172 | * Units: g (gravitational acceleration) | ||
| 173 | * | ||
| 174 | * For gyroscope state vector: | ||
| 175 | * Orientation is determined by right-hand rule. | ||
| 176 | * Units: deg/sec | ||
| 177 | * | ||
| 178 | * For rotation state vector | ||
| 179 | * Units: rotations | ||
| 180 | * | ||
| 181 | * For orientation state matrix | ||
| 182 | * x vector | ||
| 183 | * y vector | ||
| 184 | * z vector | ||
| 185 | * | ||
| 186 | * For quaternion state vector | ||
| 187 | * xyz vector | ||
| 188 | * w float | ||
| 189 | */ | ||
| 190 | using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>, | ||
| 191 | std::array<Common::Vec3f, 3>, Common::Quaternion<f32>>; | ||
| 192 | |||
| 193 | /** | ||
| 194 | * A motion device is an input device that returns a motion status object | ||
| 195 | */ | ||
| 196 | using MotionDevice = InputDevice<MotionStatus>; | ||
| 197 | |||
| 198 | /** | ||
| 199 | * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool. | ||
| 200 | * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is | ||
| 201 | * pressed. | ||
| 202 | */ | ||
| 203 | using TouchStatus = std::array<std::tuple<float, float, bool>, 16>; | ||
| 204 | |||
| 205 | /** | ||
| 206 | * A touch device is an input device that returns a touch status object | ||
| 207 | */ | ||
| 208 | using TouchDevice = InputDevice<TouchStatus>; | ||
| 209 | |||
| 210 | /** | ||
| 211 | * A mouse device is an input device that returns a tuple of two floats and four ints. | ||
| 212 | * The first two floats are X and Y device coordinates of the mouse (from 0-1). | ||
| 213 | * The s32s are the mouse wheel. | ||
| 214 | */ | ||
| 215 | using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>; | ||
| 216 | |||
| 217 | } // namespace Input | ||
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp new file mode 100644 index 000000000..685ec080c --- /dev/null +++ b/src/core/hid/emulated_console.cpp | |||
| @@ -0,0 +1,232 @@ | |||
| 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 | motion_devices->SetCallback({ | ||
| 70 | .on_change = | ||
| 71 | [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); }, | ||
| 72 | }); | ||
| 73 | } | ||
| 74 | |||
| 75 | // Unique index for identifying touch device source | ||
| 76 | std::size_t index = 0; | ||
| 77 | for (auto& touch_device : touch_devices) { | ||
| 78 | touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]); | ||
| 79 | if (!touch_device) { | ||
| 80 | continue; | ||
| 81 | } | ||
| 82 | touch_device->SetCallback({ | ||
| 83 | .on_change = | ||
| 84 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 85 | SetTouch(callback, index); | ||
| 86 | }, | ||
| 87 | }); | ||
| 88 | index++; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | void EmulatedConsole::UnloadInput() { | ||
| 93 | motion_devices.reset(); | ||
| 94 | for (auto& touch : touch_devices) { | ||
| 95 | touch.reset(); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | void EmulatedConsole::EnableConfiguration() { | ||
| 100 | is_configuring = true; | ||
| 101 | SaveCurrentConfig(); | ||
| 102 | } | ||
| 103 | |||
| 104 | void EmulatedConsole::DisableConfiguration() { | ||
| 105 | is_configuring = false; | ||
| 106 | } | ||
| 107 | |||
| 108 | bool EmulatedConsole::IsConfiguring() const { | ||
| 109 | return is_configuring; | ||
| 110 | } | ||
| 111 | |||
| 112 | void EmulatedConsole::SaveCurrentConfig() { | ||
| 113 | if (!is_configuring) { | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | void EmulatedConsole::RestoreConfig() { | ||
| 119 | if (!is_configuring) { | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | ReloadFromSettings(); | ||
| 123 | } | ||
| 124 | |||
| 125 | Common::ParamPackage EmulatedConsole::GetMotionParam() const { | ||
| 126 | return motion_params; | ||
| 127 | } | ||
| 128 | |||
| 129 | void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { | ||
| 130 | motion_params = param; | ||
| 131 | ReloadInput(); | ||
| 132 | } | ||
| 133 | |||
| 134 | void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { | ||
| 135 | std::lock_guard lock{mutex}; | ||
| 136 | auto& raw_status = console.motion_values.raw_status; | ||
| 137 | auto& emulated = console.motion_values.emulated; | ||
| 138 | |||
| 139 | raw_status = TransformToMotion(callback); | ||
| 140 | emulated.SetAcceleration(Common::Vec3f{ | ||
| 141 | raw_status.accel.x.value, | ||
| 142 | raw_status.accel.y.value, | ||
| 143 | raw_status.accel.z.value, | ||
| 144 | }); | ||
| 145 | emulated.SetGyroscope(Common::Vec3f{ | ||
| 146 | raw_status.gyro.x.value, | ||
| 147 | raw_status.gyro.y.value, | ||
| 148 | raw_status.gyro.z.value, | ||
| 149 | }); | ||
| 150 | emulated.UpdateRotation(raw_status.delta_timestamp); | ||
| 151 | emulated.UpdateOrientation(raw_status.delta_timestamp); | ||
| 152 | |||
| 153 | if (is_configuring) { | ||
| 154 | TriggerOnChange(ConsoleTriggerType::Motion); | ||
| 155 | return; | ||
| 156 | } | ||
| 157 | |||
| 158 | auto& motion = console.motion_state; | ||
| 159 | motion.accel = emulated.GetAcceleration(); | ||
| 160 | motion.gyro = emulated.GetGyroscope(); | ||
| 161 | motion.rotation = emulated.GetGyroscope(); | ||
| 162 | motion.orientation = emulated.GetOrientation(); | ||
| 163 | motion.quaternion = emulated.GetQuaternion(); | ||
| 164 | motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); | ||
| 165 | |||
| 166 | TriggerOnChange(ConsoleTriggerType::Motion); | ||
| 167 | } | ||
| 168 | |||
| 169 | void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) { | ||
| 170 | if (index >= console.touch_values.size()) { | ||
| 171 | return; | ||
| 172 | } | ||
| 173 | std::lock_guard lock{mutex}; | ||
| 174 | |||
| 175 | console.touch_values[index] = TransformToTouch(callback); | ||
| 176 | |||
| 177 | if (is_configuring) { | ||
| 178 | TriggerOnChange(ConsoleTriggerType::Touch); | ||
| 179 | return; | ||
| 180 | } | ||
| 181 | |||
| 182 | // TODO(german77): Remap touch id in sequential order | ||
| 183 | console.touch_state[index] = { | ||
| 184 | .position = {console.touch_values[index].x.value, console.touch_values[index].y.value}, | ||
| 185 | .id = static_cast<u32>(console.touch_values[index].id), | ||
| 186 | .pressed = console.touch_values[index].pressed.value, | ||
| 187 | }; | ||
| 188 | |||
| 189 | TriggerOnChange(ConsoleTriggerType::Touch); | ||
| 190 | } | ||
| 191 | |||
| 192 | ConsoleMotionValues EmulatedConsole::GetMotionValues() const { | ||
| 193 | return console.motion_values; | ||
| 194 | } | ||
| 195 | |||
| 196 | TouchValues EmulatedConsole::GetTouchValues() const { | ||
| 197 | return console.touch_values; | ||
| 198 | } | ||
| 199 | |||
| 200 | ConsoleMotion EmulatedConsole::GetMotion() const { | ||
| 201 | return console.motion_state; | ||
| 202 | } | ||
| 203 | |||
| 204 | TouchFingerState EmulatedConsole::GetTouch() const { | ||
| 205 | return console.touch_state; | ||
| 206 | } | ||
| 207 | |||
| 208 | void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { | ||
| 209 | for (const auto& poller_pair : callback_list) { | ||
| 210 | const ConsoleUpdateCallback& poller = poller_pair.second; | ||
| 211 | if (poller.on_change) { | ||
| 212 | poller.on_change(type); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { | ||
| 218 | std::lock_guard lock{mutex}; | ||
| 219 | callback_list.insert_or_assign(last_callback_key, update_callback); | ||
| 220 | return last_callback_key++; | ||
| 221 | } | ||
| 222 | |||
| 223 | void EmulatedConsole::DeleteCallback(int key) { | ||
| 224 | std::lock_guard lock{mutex}; | ||
| 225 | const auto& iterator = callback_list.find(key); | ||
| 226 | if (iterator == callback_list.end()) { | ||
| 227 | LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | callback_list.erase(iterator); | ||
| 231 | } | ||
| 232 | } // 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..3afd284d5 --- /dev/null +++ b/src/core/hid/emulated_console.h | |||
| @@ -0,0 +1,190 @@ | |||
| 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 within the emulated switch console tablet such as touch and motion | ||
| 82 | */ | ||
| 83 | explicit 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 | /** | ||
| 93 | * Sets the emulated console into configuring mode | ||
| 94 | * This prevents the modification of the HID state of the emulated console by input commands | ||
| 95 | */ | ||
| 96 | void EnableConfiguration(); | ||
| 97 | |||
| 98 | /// Returns the emulated console into normal mode, allowing the modification of the HID state | ||
| 99 | void DisableConfiguration(); | ||
| 100 | |||
| 101 | /// Returns true if the emulated console is in configuring mode | ||
| 102 | bool IsConfiguring() const; | ||
| 103 | |||
| 104 | /// Reload all input devices | ||
| 105 | void ReloadInput(); | ||
| 106 | |||
| 107 | /// Overrides current mapped devices with the stored configuration and reloads all input devices | ||
| 108 | void ReloadFromSettings(); | ||
| 109 | |||
| 110 | /// Saves the current mapped configuration | ||
| 111 | void SaveCurrentConfig(); | ||
| 112 | |||
| 113 | /// Reverts any mapped changes made that weren't saved | ||
| 114 | void RestoreConfig(); | ||
| 115 | |||
| 116 | // Returns the current mapped motion device | ||
| 117 | Common::ParamPackage GetMotionParam() const; | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Updates the current mapped motion device | ||
| 121 | * @param param ParamPackage with controller data to be mapped | ||
| 122 | */ | ||
| 123 | void SetMotionParam(Common::ParamPackage param); | ||
| 124 | |||
| 125 | /// Returns the latest status of motion input from the console with parameters | ||
| 126 | ConsoleMotionValues GetMotionValues() const; | ||
| 127 | |||
| 128 | /// Returns the latest status of touch input from the console with parameters | ||
| 129 | TouchValues GetTouchValues() const; | ||
| 130 | |||
| 131 | /// Returns the latest status of motion input from the console | ||
| 132 | ConsoleMotion GetMotion() const; | ||
| 133 | |||
| 134 | /// Returns the latest status of touch input from the console | ||
| 135 | TouchFingerState GetTouch() const; | ||
| 136 | |||
| 137 | /** | ||
| 138 | * Adds a callback to the list of events | ||
| 139 | * @param update_callback A ConsoleUpdateCallback that will be triggered | ||
| 140 | * @return an unique key corresponding to the callback index in the list | ||
| 141 | */ | ||
| 142 | int SetCallback(ConsoleUpdateCallback update_callback); | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Removes a callback from the list stopping any future events to this object | ||
| 146 | * @param key Key corresponding to the callback index in the list | ||
| 147 | */ | ||
| 148 | void DeleteCallback(int key); | ||
| 149 | |||
| 150 | private: | ||
| 151 | /// Creates and stores the touch params | ||
| 152 | void SetTouchParams(); | ||
| 153 | |||
| 154 | /** | ||
| 155 | * Updates the motion status of the console | ||
| 156 | * @param callback A CallbackStatus containing gyro and accelerometer data | ||
| 157 | */ | ||
| 158 | void SetMotion(const Common::Input::CallbackStatus& callback); | ||
| 159 | |||
| 160 | /** | ||
| 161 | * Updates the touch status of the console | ||
| 162 | * @param callback A CallbackStatus containing the touch position | ||
| 163 | * @param index Finger ID to be updated | ||
| 164 | */ | ||
| 165 | void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index); | ||
| 166 | |||
| 167 | /** | ||
| 168 | * Triggers a callback that something has changed on the console status | ||
| 169 | * @param type Input type of the event to trigger | ||
| 170 | */ | ||
| 171 | void TriggerOnChange(ConsoleTriggerType type); | ||
| 172 | |||
| 173 | bool is_configuring{false}; | ||
| 174 | f32 motion_sensitivity{0.01f}; | ||
| 175 | |||
| 176 | ConsoleMotionParams motion_params; | ||
| 177 | TouchParams touch_params; | ||
| 178 | |||
| 179 | ConsoleMotionDevices motion_devices; | ||
| 180 | TouchDevices touch_devices; | ||
| 181 | |||
| 182 | mutable std::mutex mutex; | ||
| 183 | std::unordered_map<int, ConsoleUpdateCallback> callback_list; | ||
| 184 | int last_callback_key = 0; | ||
| 185 | |||
| 186 | // Stores the current status of all console input | ||
| 187 | ConsoleStatus console; | ||
| 188 | }; | ||
| 189 | |||
| 190 | } // 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..93372445b --- /dev/null +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -0,0 +1,1139 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included | ||
| 4 | |||
| 5 | #include "core/hid/emulated_controller.h" | ||
| 6 | #include "core/hid/input_converter.h" | ||
| 7 | |||
| 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 | case Settings::ControllerType::Pokeball: | ||
| 31 | return NpadStyleIndex::Pokeball; | ||
| 32 | case Settings::ControllerType::NES: | ||
| 33 | return NpadStyleIndex::NES; | ||
| 34 | case Settings::ControllerType::SNES: | ||
| 35 | return NpadStyleIndex::SNES; | ||
| 36 | case Settings::ControllerType::N64: | ||
| 37 | return NpadStyleIndex::N64; | ||
| 38 | case Settings::ControllerType::SegaGenesis: | ||
| 39 | return NpadStyleIndex::SegaGenesis; | ||
| 40 | default: | ||
| 41 | return NpadStyleIndex::ProController; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) { | ||
| 46 | switch (type) { | ||
| 47 | case NpadStyleIndex::ProController: | ||
| 48 | return Settings::ControllerType::ProController; | ||
| 49 | case NpadStyleIndex::JoyconDual: | ||
| 50 | return Settings::ControllerType::DualJoyconDetached; | ||
| 51 | case NpadStyleIndex::JoyconLeft: | ||
| 52 | return Settings::ControllerType::LeftJoycon; | ||
| 53 | case NpadStyleIndex::JoyconRight: | ||
| 54 | return Settings::ControllerType::RightJoycon; | ||
| 55 | case NpadStyleIndex::Handheld: | ||
| 56 | return Settings::ControllerType::Handheld; | ||
| 57 | case NpadStyleIndex::GameCube: | ||
| 58 | return Settings::ControllerType::GameCube; | ||
| 59 | case NpadStyleIndex::Pokeball: | ||
| 60 | return Settings::ControllerType::Pokeball; | ||
| 61 | case NpadStyleIndex::NES: | ||
| 62 | return Settings::ControllerType::NES; | ||
| 63 | case NpadStyleIndex::SNES: | ||
| 64 | return Settings::ControllerType::SNES; | ||
| 65 | case NpadStyleIndex::N64: | ||
| 66 | return Settings::ControllerType::N64; | ||
| 67 | case NpadStyleIndex::SegaGenesis: | ||
| 68 | return Settings::ControllerType::SegaGenesis; | ||
| 69 | default: | ||
| 70 | return Settings::ControllerType::ProController; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | void EmulatedController::ReloadFromSettings() { | ||
| 75 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 76 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 77 | |||
| 78 | for (std::size_t index = 0; index < player.buttons.size(); ++index) { | ||
| 79 | button_params[index] = Common::ParamPackage(player.buttons[index]); | ||
| 80 | } | ||
| 81 | for (std::size_t index = 0; index < player.analogs.size(); ++index) { | ||
| 82 | stick_params[index] = Common::ParamPackage(player.analogs[index]); | ||
| 83 | } | ||
| 84 | for (std::size_t index = 0; index < player.motions.size(); ++index) { | ||
| 85 | motion_params[index] = Common::ParamPackage(player.motions[index]); | ||
| 86 | } | ||
| 87 | |||
| 88 | controller.colors_state.left = { | ||
| 89 | .body = player.body_color_left, | ||
| 90 | .button = player.button_color_left, | ||
| 91 | }; | ||
| 92 | |||
| 93 | controller.colors_state.right = { | ||
| 94 | .body = player.body_color_right, | ||
| 95 | .button = player.button_color_right, | ||
| 96 | }; | ||
| 97 | |||
| 98 | controller.colors_state.fullkey = controller.colors_state.left; | ||
| 99 | |||
| 100 | // Other or debug controller should always be a pro controller | ||
| 101 | if (npad_id_type != NpadIdType::Other) { | ||
| 102 | SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); | ||
| 103 | } else { | ||
| 104 | SetNpadStyleIndex(NpadStyleIndex::ProController); | ||
| 105 | } | ||
| 106 | |||
| 107 | if (player.connected) { | ||
| 108 | Connect(); | ||
| 109 | } else { | ||
| 110 | Disconnect(); | ||
| 111 | } | ||
| 112 | |||
| 113 | ReloadInput(); | ||
| 114 | } | ||
| 115 | |||
| 116 | void EmulatedController::LoadDevices() { | ||
| 117 | // TODO(german77): Use more buttons to detect the correct device | ||
| 118 | const auto left_joycon = button_params[Settings::NativeButton::DRight]; | ||
| 119 | const auto right_joycon = button_params[Settings::NativeButton::A]; | ||
| 120 | |||
| 121 | // Triggers for GC controllers | ||
| 122 | trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; | ||
| 123 | trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; | ||
| 124 | |||
| 125 | battery_params[LeftIndex] = left_joycon; | ||
| 126 | battery_params[RightIndex] = right_joycon; | ||
| 127 | battery_params[LeftIndex].Set("battery", true); | ||
| 128 | battery_params[RightIndex].Set("battery", true); | ||
| 129 | |||
| 130 | output_params[LeftIndex] = left_joycon; | ||
| 131 | output_params[RightIndex] = right_joycon; | ||
| 132 | output_params[LeftIndex].Set("output", true); | ||
| 133 | output_params[RightIndex].Set("output", true); | ||
| 134 | |||
| 135 | LoadTASParams(); | ||
| 136 | |||
| 137 | std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, | ||
| 138 | button_params.begin() + Settings::NativeButton::BUTTON_NS_END, | ||
| 139 | button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 140 | std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, | ||
| 141 | stick_params.begin() + Settings::NativeAnalog::STICK_HID_END, | ||
| 142 | stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 143 | std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, | ||
| 144 | motion_params.begin() + Settings::NativeMotion::MOTION_HID_END, | ||
| 145 | motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 146 | std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(), | ||
| 147 | Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 148 | std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(), | ||
| 149 | Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 150 | std::transform(output_params.begin(), output_params.end(), output_devices.begin(), | ||
| 151 | Common::Input::CreateDevice<Common::Input::OutputDevice>); | ||
| 152 | |||
| 153 | // Initialize TAS devices | ||
| 154 | std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(), | ||
| 155 | Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 156 | std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(), | ||
| 157 | Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 158 | } | ||
| 159 | |||
| 160 | void EmulatedController::LoadTASParams() { | ||
| 161 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 162 | Common::ParamPackage common_params{}; | ||
| 163 | common_params.Set("engine", "tas"); | ||
| 164 | common_params.Set("port", static_cast<int>(player_index)); | ||
| 165 | for (auto& param : tas_button_params) { | ||
| 166 | param = common_params; | ||
| 167 | } | ||
| 168 | for (auto& param : tas_stick_params) { | ||
| 169 | param = common_params; | ||
| 170 | } | ||
| 171 | |||
| 172 | // TODO(german77): Replace this with an input profile or something better | ||
| 173 | tas_button_params[Settings::NativeButton::A].Set("button", 0); | ||
| 174 | tas_button_params[Settings::NativeButton::B].Set("button", 1); | ||
| 175 | tas_button_params[Settings::NativeButton::X].Set("button", 2); | ||
| 176 | tas_button_params[Settings::NativeButton::Y].Set("button", 3); | ||
| 177 | tas_button_params[Settings::NativeButton::LStick].Set("button", 4); | ||
| 178 | tas_button_params[Settings::NativeButton::RStick].Set("button", 5); | ||
| 179 | tas_button_params[Settings::NativeButton::L].Set("button", 6); | ||
| 180 | tas_button_params[Settings::NativeButton::R].Set("button", 7); | ||
| 181 | tas_button_params[Settings::NativeButton::ZL].Set("button", 8); | ||
| 182 | tas_button_params[Settings::NativeButton::ZR].Set("button", 9); | ||
| 183 | tas_button_params[Settings::NativeButton::Plus].Set("button", 10); | ||
| 184 | tas_button_params[Settings::NativeButton::Minus].Set("button", 11); | ||
| 185 | tas_button_params[Settings::NativeButton::DLeft].Set("button", 12); | ||
| 186 | tas_button_params[Settings::NativeButton::DUp].Set("button", 13); | ||
| 187 | tas_button_params[Settings::NativeButton::DRight].Set("button", 14); | ||
| 188 | tas_button_params[Settings::NativeButton::DDown].Set("button", 15); | ||
| 189 | tas_button_params[Settings::NativeButton::SL].Set("button", 16); | ||
| 190 | tas_button_params[Settings::NativeButton::SR].Set("button", 17); | ||
| 191 | tas_button_params[Settings::NativeButton::Home].Set("button", 18); | ||
| 192 | tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); | ||
| 193 | |||
| 194 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); | ||
| 195 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); | ||
| 196 | tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); | ||
| 197 | tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); | ||
| 198 | } | ||
| 199 | |||
| 200 | void EmulatedController::ReloadInput() { | ||
| 201 | // If you load any device here add the equivalent to the UnloadInput() function | ||
| 202 | LoadDevices(); | ||
| 203 | for (std::size_t index = 0; index < button_devices.size(); ++index) { | ||
| 204 | if (!button_devices[index]) { | ||
| 205 | continue; | ||
| 206 | } | ||
| 207 | const auto uuid = Common::UUID{button_params[index].Get("guid", "")}; | ||
| 208 | button_devices[index]->SetCallback({ | ||
| 209 | .on_change = | ||
| 210 | [this, index, uuid](const Common::Input::CallbackStatus& callback) { | ||
| 211 | SetButton(callback, index, uuid); | ||
| 212 | }, | ||
| 213 | }); | ||
| 214 | button_devices[index]->ForceUpdate(); | ||
| 215 | } | ||
| 216 | |||
| 217 | for (std::size_t index = 0; index < stick_devices.size(); ++index) { | ||
| 218 | if (!stick_devices[index]) { | ||
| 219 | continue; | ||
| 220 | } | ||
| 221 | const auto uuid = Common::UUID{stick_params[index].Get("guid", "")}; | ||
| 222 | stick_devices[index]->SetCallback({ | ||
| 223 | .on_change = | ||
| 224 | [this, index, uuid](const Common::Input::CallbackStatus& callback) { | ||
| 225 | SetStick(callback, index, uuid); | ||
| 226 | }, | ||
| 227 | }); | ||
| 228 | stick_devices[index]->ForceUpdate(); | ||
| 229 | } | ||
| 230 | |||
| 231 | for (std::size_t index = 0; index < trigger_devices.size(); ++index) { | ||
| 232 | if (!trigger_devices[index]) { | ||
| 233 | continue; | ||
| 234 | } | ||
| 235 | const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")}; | ||
| 236 | trigger_devices[index]->SetCallback({ | ||
| 237 | .on_change = | ||
| 238 | [this, index, uuid](const Common::Input::CallbackStatus& callback) { | ||
| 239 | SetTrigger(callback, index, uuid); | ||
| 240 | }, | ||
| 241 | }); | ||
| 242 | trigger_devices[index]->ForceUpdate(); | ||
| 243 | } | ||
| 244 | |||
| 245 | for (std::size_t index = 0; index < battery_devices.size(); ++index) { | ||
| 246 | if (!battery_devices[index]) { | ||
| 247 | continue; | ||
| 248 | } | ||
| 249 | battery_devices[index]->SetCallback({ | ||
| 250 | .on_change = | ||
| 251 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 252 | SetBattery(callback, index); | ||
| 253 | }, | ||
| 254 | }); | ||
| 255 | battery_devices[index]->ForceUpdate(); | ||
| 256 | } | ||
| 257 | |||
| 258 | for (std::size_t index = 0; index < motion_devices.size(); ++index) { | ||
| 259 | if (!motion_devices[index]) { | ||
| 260 | continue; | ||
| 261 | } | ||
| 262 | motion_devices[index]->SetCallback({ | ||
| 263 | .on_change = | ||
| 264 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 265 | SetMotion(callback, index); | ||
| 266 | }, | ||
| 267 | }); | ||
| 268 | motion_devices[index]->ForceUpdate(); | ||
| 269 | } | ||
| 270 | |||
| 271 | // Use a common UUID for TAS | ||
| 272 | const auto tas_uuid = Common::UUID{0x0, 0x7A5}; | ||
| 273 | |||
| 274 | // Register TAS devices. No need to force update | ||
| 275 | for (std::size_t index = 0; index < tas_button_devices.size(); ++index) { | ||
| 276 | if (!tas_button_devices[index]) { | ||
| 277 | continue; | ||
| 278 | } | ||
| 279 | tas_button_devices[index]->SetCallback({ | ||
| 280 | .on_change = | ||
| 281 | [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) { | ||
| 282 | SetButton(callback, index, tas_uuid); | ||
| 283 | }, | ||
| 284 | }); | ||
| 285 | } | ||
| 286 | |||
| 287 | for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) { | ||
| 288 | if (!tas_stick_devices[index]) { | ||
| 289 | continue; | ||
| 290 | } | ||
| 291 | tas_stick_devices[index]->SetCallback({ | ||
| 292 | .on_change = | ||
| 293 | [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) { | ||
| 294 | SetStick(callback, index, tas_uuid); | ||
| 295 | }, | ||
| 296 | }); | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | void EmulatedController::UnloadInput() { | ||
| 301 | for (auto& button : button_devices) { | ||
| 302 | button.reset(); | ||
| 303 | } | ||
| 304 | for (auto& stick : stick_devices) { | ||
| 305 | stick.reset(); | ||
| 306 | } | ||
| 307 | for (auto& motion : motion_devices) { | ||
| 308 | motion.reset(); | ||
| 309 | } | ||
| 310 | for (auto& trigger : trigger_devices) { | ||
| 311 | trigger.reset(); | ||
| 312 | } | ||
| 313 | for (auto& battery : battery_devices) { | ||
| 314 | battery.reset(); | ||
| 315 | } | ||
| 316 | for (auto& output : output_devices) { | ||
| 317 | output.reset(); | ||
| 318 | } | ||
| 319 | for (auto& button : tas_button_devices) { | ||
| 320 | button.reset(); | ||
| 321 | } | ||
| 322 | for (auto& stick : tas_stick_devices) { | ||
| 323 | stick.reset(); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | void EmulatedController::EnableConfiguration() { | ||
| 328 | is_configuring = true; | ||
| 329 | tmp_is_connected = is_connected; | ||
| 330 | tmp_npad_type = npad_type; | ||
| 331 | } | ||
| 332 | |||
| 333 | void EmulatedController::DisableConfiguration() { | ||
| 334 | is_configuring = false; | ||
| 335 | |||
| 336 | // Apply temporary npad type to the real controller | ||
| 337 | if (tmp_npad_type != npad_type) { | ||
| 338 | if (is_connected) { | ||
| 339 | Disconnect(); | ||
| 340 | } | ||
| 341 | SetNpadStyleIndex(tmp_npad_type); | ||
| 342 | } | ||
| 343 | |||
| 344 | // Apply temporary connected status to the real controller | ||
| 345 | if (tmp_is_connected != is_connected) { | ||
| 346 | if (tmp_is_connected) { | ||
| 347 | Connect(); | ||
| 348 | return; | ||
| 349 | } | ||
| 350 | Disconnect(); | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | bool EmulatedController::IsConfiguring() const { | ||
| 355 | return is_configuring; | ||
| 356 | } | ||
| 357 | |||
| 358 | void EmulatedController::SaveCurrentConfig() { | ||
| 359 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 360 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 361 | player.connected = is_connected; | ||
| 362 | player.controller_type = MapNPadToSettingsType(npad_type); | ||
| 363 | for (std::size_t index = 0; index < player.buttons.size(); ++index) { | ||
| 364 | player.buttons[index] = button_params[index].Serialize(); | ||
| 365 | } | ||
| 366 | for (std::size_t index = 0; index < player.analogs.size(); ++index) { | ||
| 367 | player.analogs[index] = stick_params[index].Serialize(); | ||
| 368 | } | ||
| 369 | for (std::size_t index = 0; index < player.motions.size(); ++index) { | ||
| 370 | player.motions[index] = motion_params[index].Serialize(); | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | void EmulatedController::RestoreConfig() { | ||
| 375 | if (!is_configuring) { | ||
| 376 | return; | ||
| 377 | } | ||
| 378 | ReloadFromSettings(); | ||
| 379 | } | ||
| 380 | |||
| 381 | std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices( | ||
| 382 | EmulatedDeviceIndex device_index) const { | ||
| 383 | std::vector<Common::ParamPackage> devices; | ||
| 384 | for (const auto& param : button_params) { | ||
| 385 | if (!param.Has("engine")) { | ||
| 386 | continue; | ||
| 387 | } | ||
| 388 | const auto devices_it = std::find_if( | ||
| 389 | devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { | ||
| 390 | return param.Get("engine", "") == param_.Get("engine", "") && | ||
| 391 | param.Get("guid", "") == param_.Get("guid", "") && | ||
| 392 | param.Get("port", 0) == param_.Get("port", 0); | ||
| 393 | }); | ||
| 394 | if (devices_it != devices.end()) { | ||
| 395 | continue; | ||
| 396 | } | ||
| 397 | Common::ParamPackage device{}; | ||
| 398 | device.Set("engine", param.Get("engine", "")); | ||
| 399 | device.Set("guid", param.Get("guid", "")); | ||
| 400 | device.Set("port", param.Get("port", 0)); | ||
| 401 | devices.push_back(device); | ||
| 402 | } | ||
| 403 | |||
| 404 | for (const auto& param : stick_params) { | ||
| 405 | if (!param.Has("engine")) { | ||
| 406 | continue; | ||
| 407 | } | ||
| 408 | if (param.Get("engine", "") == "analog_from_button") { | ||
| 409 | continue; | ||
| 410 | } | ||
| 411 | const auto devices_it = std::find_if( | ||
| 412 | devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { | ||
| 413 | return param.Get("engine", "") == param_.Get("engine", "") && | ||
| 414 | param.Get("guid", "") == param_.Get("guid", "") && | ||
| 415 | param.Get("port", 0) == param_.Get("port", 0); | ||
| 416 | }); | ||
| 417 | if (devices_it != devices.end()) { | ||
| 418 | continue; | ||
| 419 | } | ||
| 420 | Common::ParamPackage device{}; | ||
| 421 | device.Set("engine", param.Get("engine", "")); | ||
| 422 | device.Set("guid", param.Get("guid", "")); | ||
| 423 | device.Set("port", param.Get("port", 0)); | ||
| 424 | devices.push_back(device); | ||
| 425 | } | ||
| 426 | return devices; | ||
| 427 | } | ||
| 428 | |||
| 429 | Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const { | ||
| 430 | if (index >= button_params.size()) { | ||
| 431 | return {}; | ||
| 432 | } | ||
| 433 | return button_params[index]; | ||
| 434 | } | ||
| 435 | |||
| 436 | Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const { | ||
| 437 | if (index >= stick_params.size()) { | ||
| 438 | return {}; | ||
| 439 | } | ||
| 440 | return stick_params[index]; | ||
| 441 | } | ||
| 442 | |||
| 443 | Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const { | ||
| 444 | if (index >= motion_params.size()) { | ||
| 445 | return {}; | ||
| 446 | } | ||
| 447 | return motion_params[index]; | ||
| 448 | } | ||
| 449 | |||
| 450 | void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) { | ||
| 451 | if (index >= button_params.size()) { | ||
| 452 | return; | ||
| 453 | } | ||
| 454 | button_params[index] = std::move(param); | ||
| 455 | ReloadInput(); | ||
| 456 | } | ||
| 457 | |||
| 458 | void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) { | ||
| 459 | if (index >= stick_params.size()) { | ||
| 460 | return; | ||
| 461 | } | ||
| 462 | stick_params[index] = std::move(param); | ||
| 463 | ReloadInput(); | ||
| 464 | } | ||
| 465 | |||
| 466 | void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) { | ||
| 467 | if (index >= motion_params.size()) { | ||
| 468 | return; | ||
| 469 | } | ||
| 470 | motion_params[index] = std::move(param); | ||
| 471 | ReloadInput(); | ||
| 472 | } | ||
| 473 | |||
| 474 | void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, | ||
| 475 | Common::UUID uuid) { | ||
| 476 | if (index >= controller.button_values.size()) { | ||
| 477 | return; | ||
| 478 | } | ||
| 479 | { | ||
| 480 | std::lock_guard lock{mutex}; | ||
| 481 | bool value_changed = false; | ||
| 482 | const auto new_status = TransformToButton(callback); | ||
| 483 | auto& current_status = controller.button_values[index]; | ||
| 484 | |||
| 485 | // Only read button values that have the same uuid or are pressed once | ||
| 486 | if (current_status.uuid != uuid) { | ||
| 487 | if (!new_status.value) { | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | current_status.toggle = new_status.toggle; | ||
| 493 | current_status.uuid = uuid; | ||
| 494 | |||
| 495 | // Update button status with current | ||
| 496 | if (!current_status.toggle) { | ||
| 497 | current_status.locked = false; | ||
| 498 | if (current_status.value != new_status.value) { | ||
| 499 | current_status.value = new_status.value; | ||
| 500 | value_changed = true; | ||
| 501 | } | ||
| 502 | } else { | ||
| 503 | // Toggle button and lock status | ||
| 504 | if (new_status.value && !current_status.locked) { | ||
| 505 | current_status.locked = true; | ||
| 506 | current_status.value = !current_status.value; | ||
| 507 | value_changed = true; | ||
| 508 | } | ||
| 509 | |||
| 510 | // Unlock button ready for next press | ||
| 511 | if (!new_status.value && current_status.locked) { | ||
| 512 | current_status.locked = false; | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | if (!value_changed) { | ||
| 517 | return; | ||
| 518 | } | ||
| 519 | |||
| 520 | if (is_configuring) { | ||
| 521 | controller.npad_button_state.raw = NpadButton::None; | ||
| 522 | controller.debug_pad_button_state.raw = 0; | ||
| 523 | TriggerOnChange(ControllerTriggerType::Button, false); | ||
| 524 | return; | ||
| 525 | } | ||
| 526 | |||
| 527 | switch (index) { | ||
| 528 | case Settings::NativeButton::A: | ||
| 529 | controller.npad_button_state.a.Assign(current_status.value); | ||
| 530 | controller.debug_pad_button_state.a.Assign(current_status.value); | ||
| 531 | break; | ||
| 532 | case Settings::NativeButton::B: | ||
| 533 | controller.npad_button_state.b.Assign(current_status.value); | ||
| 534 | controller.debug_pad_button_state.b.Assign(current_status.value); | ||
| 535 | break; | ||
| 536 | case Settings::NativeButton::X: | ||
| 537 | controller.npad_button_state.x.Assign(current_status.value); | ||
| 538 | controller.debug_pad_button_state.x.Assign(current_status.value); | ||
| 539 | break; | ||
| 540 | case Settings::NativeButton::Y: | ||
| 541 | controller.npad_button_state.y.Assign(current_status.value); | ||
| 542 | controller.debug_pad_button_state.y.Assign(current_status.value); | ||
| 543 | break; | ||
| 544 | case Settings::NativeButton::LStick: | ||
| 545 | controller.npad_button_state.stick_l.Assign(current_status.value); | ||
| 546 | break; | ||
| 547 | case Settings::NativeButton::RStick: | ||
| 548 | controller.npad_button_state.stick_r.Assign(current_status.value); | ||
| 549 | break; | ||
| 550 | case Settings::NativeButton::L: | ||
| 551 | controller.npad_button_state.l.Assign(current_status.value); | ||
| 552 | controller.debug_pad_button_state.l.Assign(current_status.value); | ||
| 553 | break; | ||
| 554 | case Settings::NativeButton::R: | ||
| 555 | controller.npad_button_state.r.Assign(current_status.value); | ||
| 556 | controller.debug_pad_button_state.r.Assign(current_status.value); | ||
| 557 | break; | ||
| 558 | case Settings::NativeButton::ZL: | ||
| 559 | controller.npad_button_state.zl.Assign(current_status.value); | ||
| 560 | controller.debug_pad_button_state.zl.Assign(current_status.value); | ||
| 561 | break; | ||
| 562 | case Settings::NativeButton::ZR: | ||
| 563 | controller.npad_button_state.zr.Assign(current_status.value); | ||
| 564 | controller.debug_pad_button_state.zr.Assign(current_status.value); | ||
| 565 | break; | ||
| 566 | case Settings::NativeButton::Plus: | ||
| 567 | controller.npad_button_state.plus.Assign(current_status.value); | ||
| 568 | controller.debug_pad_button_state.plus.Assign(current_status.value); | ||
| 569 | break; | ||
| 570 | case Settings::NativeButton::Minus: | ||
| 571 | controller.npad_button_state.minus.Assign(current_status.value); | ||
| 572 | controller.debug_pad_button_state.minus.Assign(current_status.value); | ||
| 573 | break; | ||
| 574 | case Settings::NativeButton::DLeft: | ||
| 575 | controller.npad_button_state.left.Assign(current_status.value); | ||
| 576 | controller.debug_pad_button_state.d_left.Assign(current_status.value); | ||
| 577 | break; | ||
| 578 | case Settings::NativeButton::DUp: | ||
| 579 | controller.npad_button_state.up.Assign(current_status.value); | ||
| 580 | controller.debug_pad_button_state.d_up.Assign(current_status.value); | ||
| 581 | break; | ||
| 582 | case Settings::NativeButton::DRight: | ||
| 583 | controller.npad_button_state.right.Assign(current_status.value); | ||
| 584 | controller.debug_pad_button_state.d_right.Assign(current_status.value); | ||
| 585 | break; | ||
| 586 | case Settings::NativeButton::DDown: | ||
| 587 | controller.npad_button_state.down.Assign(current_status.value); | ||
| 588 | controller.debug_pad_button_state.d_down.Assign(current_status.value); | ||
| 589 | break; | ||
| 590 | case Settings::NativeButton::SL: | ||
| 591 | controller.npad_button_state.left_sl.Assign(current_status.value); | ||
| 592 | controller.npad_button_state.right_sl.Assign(current_status.value); | ||
| 593 | break; | ||
| 594 | case Settings::NativeButton::SR: | ||
| 595 | controller.npad_button_state.left_sr.Assign(current_status.value); | ||
| 596 | controller.npad_button_state.right_sr.Assign(current_status.value); | ||
| 597 | break; | ||
| 598 | case Settings::NativeButton::Home: | ||
| 599 | case Settings::NativeButton::Screenshot: | ||
| 600 | break; | ||
| 601 | } | ||
| 602 | } | ||
| 603 | if (!is_connected) { | ||
| 604 | if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { | ||
| 605 | Connect(); | ||
| 606 | } | ||
| 607 | if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) { | ||
| 608 | Connect(); | ||
| 609 | } | ||
| 610 | } | ||
| 611 | TriggerOnChange(ControllerTriggerType::Button, true); | ||
| 612 | } | ||
| 613 | |||
| 614 | void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index, | ||
| 615 | Common::UUID uuid) { | ||
| 616 | if (index >= controller.stick_values.size()) { | ||
| 617 | return; | ||
| 618 | } | ||
| 619 | std::lock_guard lock{mutex}; | ||
| 620 | const auto stick_value = TransformToStick(callback); | ||
| 621 | |||
| 622 | // Only read stick values that have the same uuid or are over the threshold to avoid flapping | ||
| 623 | if (controller.stick_values[index].uuid != uuid) { | ||
| 624 | if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) { | ||
| 625 | return; | ||
| 626 | } | ||
| 627 | } | ||
| 628 | |||
| 629 | controller.stick_values[index] = stick_value; | ||
| 630 | controller.stick_values[index].uuid = uuid; | ||
| 631 | |||
| 632 | if (is_configuring) { | ||
| 633 | controller.analog_stick_state.left = {}; | ||
| 634 | controller.analog_stick_state.right = {}; | ||
| 635 | TriggerOnChange(ControllerTriggerType::Stick, false); | ||
| 636 | return; | ||
| 637 | } | ||
| 638 | |||
| 639 | const AnalogStickState stick{ | ||
| 640 | .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX), | ||
| 641 | .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX), | ||
| 642 | }; | ||
| 643 | |||
| 644 | switch (index) { | ||
| 645 | case Settings::NativeAnalog::LStick: | ||
| 646 | controller.analog_stick_state.left = stick; | ||
| 647 | controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left); | ||
| 648 | controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up); | ||
| 649 | controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right); | ||
| 650 | controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down); | ||
| 651 | break; | ||
| 652 | case Settings::NativeAnalog::RStick: | ||
| 653 | controller.analog_stick_state.right = stick; | ||
| 654 | controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left); | ||
| 655 | controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up); | ||
| 656 | controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right); | ||
| 657 | controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); | ||
| 658 | break; | ||
| 659 | } | ||
| 660 | |||
| 661 | TriggerOnChange(ControllerTriggerType::Stick, true); | ||
| 662 | } | ||
| 663 | |||
| 664 | void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback, | ||
| 665 | std::size_t index, Common::UUID uuid) { | ||
| 666 | if (index >= controller.trigger_values.size()) { | ||
| 667 | return; | ||
| 668 | } | ||
| 669 | std::lock_guard lock{mutex}; | ||
| 670 | const auto trigger_value = TransformToTrigger(callback); | ||
| 671 | |||
| 672 | // Only read trigger values that have the same uuid or are pressed once | ||
| 673 | if (controller.trigger_values[index].uuid != uuid) { | ||
| 674 | if (!trigger_value.pressed.value) { | ||
| 675 | return; | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | controller.trigger_values[index] = trigger_value; | ||
| 680 | controller.trigger_values[index].uuid = uuid; | ||
| 681 | |||
| 682 | if (is_configuring) { | ||
| 683 | controller.gc_trigger_state.left = 0; | ||
| 684 | controller.gc_trigger_state.right = 0; | ||
| 685 | TriggerOnChange(ControllerTriggerType::Trigger, false); | ||
| 686 | return; | ||
| 687 | } | ||
| 688 | |||
| 689 | const auto& trigger = controller.trigger_values[index]; | ||
| 690 | |||
| 691 | switch (index) { | ||
| 692 | case Settings::NativeTrigger::LTrigger: | ||
| 693 | controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX); | ||
| 694 | controller.npad_button_state.zl.Assign(trigger.pressed.value); | ||
| 695 | break; | ||
| 696 | case Settings::NativeTrigger::RTrigger: | ||
| 697 | controller.gc_trigger_state.right = | ||
| 698 | static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX); | ||
| 699 | controller.npad_button_state.zr.Assign(trigger.pressed.value); | ||
| 700 | break; | ||
| 701 | } | ||
| 702 | |||
| 703 | TriggerOnChange(ControllerTriggerType::Trigger, true); | ||
| 704 | } | ||
| 705 | |||
| 706 | void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback, | ||
| 707 | std::size_t index) { | ||
| 708 | if (index >= controller.motion_values.size()) { | ||
| 709 | return; | ||
| 710 | } | ||
| 711 | std::lock_guard lock{mutex}; | ||
| 712 | auto& raw_status = controller.motion_values[index].raw_status; | ||
| 713 | auto& emulated = controller.motion_values[index].emulated; | ||
| 714 | |||
| 715 | raw_status = TransformToMotion(callback); | ||
| 716 | emulated.SetAcceleration(Common::Vec3f{ | ||
| 717 | raw_status.accel.x.value, | ||
| 718 | raw_status.accel.y.value, | ||
| 719 | raw_status.accel.z.value, | ||
| 720 | }); | ||
| 721 | emulated.SetGyroscope(Common::Vec3f{ | ||
| 722 | raw_status.gyro.x.value, | ||
| 723 | raw_status.gyro.y.value, | ||
| 724 | raw_status.gyro.z.value, | ||
| 725 | }); | ||
| 726 | emulated.UpdateRotation(raw_status.delta_timestamp); | ||
| 727 | emulated.UpdateOrientation(raw_status.delta_timestamp); | ||
| 728 | force_update_motion = raw_status.force_update; | ||
| 729 | |||
| 730 | if (is_configuring) { | ||
| 731 | TriggerOnChange(ControllerTriggerType::Motion, false); | ||
| 732 | return; | ||
| 733 | } | ||
| 734 | |||
| 735 | auto& motion = controller.motion_state[index]; | ||
| 736 | motion.accel = emulated.GetAcceleration(); | ||
| 737 | motion.gyro = emulated.GetGyroscope(); | ||
| 738 | motion.rotation = emulated.GetRotations(); | ||
| 739 | motion.orientation = emulated.GetOrientation(); | ||
| 740 | motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); | ||
| 741 | |||
| 742 | TriggerOnChange(ControllerTriggerType::Motion, true); | ||
| 743 | } | ||
| 744 | |||
| 745 | void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, | ||
| 746 | std::size_t index) { | ||
| 747 | if (index >= controller.battery_values.size()) { | ||
| 748 | return; | ||
| 749 | } | ||
| 750 | std::lock_guard lock{mutex}; | ||
| 751 | controller.battery_values[index] = TransformToBattery(callback); | ||
| 752 | |||
| 753 | if (is_configuring) { | ||
| 754 | TriggerOnChange(ControllerTriggerType::Battery, false); | ||
| 755 | return; | ||
| 756 | } | ||
| 757 | |||
| 758 | bool is_charging = false; | ||
| 759 | bool is_powered = false; | ||
| 760 | NpadBatteryLevel battery_level = 0; | ||
| 761 | switch (controller.battery_values[index]) { | ||
| 762 | case Common::Input::BatteryLevel::Charging: | ||
| 763 | is_charging = true; | ||
| 764 | is_powered = true; | ||
| 765 | battery_level = 6; | ||
| 766 | break; | ||
| 767 | case Common::Input::BatteryLevel::Medium: | ||
| 768 | battery_level = 6; | ||
| 769 | break; | ||
| 770 | case Common::Input::BatteryLevel::Low: | ||
| 771 | battery_level = 4; | ||
| 772 | break; | ||
| 773 | case Common::Input::BatteryLevel::Critical: | ||
| 774 | battery_level = 2; | ||
| 775 | break; | ||
| 776 | case Common::Input::BatteryLevel::Empty: | ||
| 777 | battery_level = 0; | ||
| 778 | break; | ||
| 779 | case Common::Input::BatteryLevel::None: | ||
| 780 | case Common::Input::BatteryLevel::Full: | ||
| 781 | default: | ||
| 782 | is_powered = true; | ||
| 783 | battery_level = 8; | ||
| 784 | break; | ||
| 785 | } | ||
| 786 | |||
| 787 | switch (index) { | ||
| 788 | case LeftIndex: | ||
| 789 | controller.battery_state.left = { | ||
| 790 | .is_powered = is_powered, | ||
| 791 | .is_charging = is_charging, | ||
| 792 | .battery_level = battery_level, | ||
| 793 | }; | ||
| 794 | break; | ||
| 795 | case RightIndex: | ||
| 796 | controller.battery_state.right = { | ||
| 797 | .is_powered = is_powered, | ||
| 798 | .is_charging = is_charging, | ||
| 799 | .battery_level = battery_level, | ||
| 800 | }; | ||
| 801 | break; | ||
| 802 | case DualIndex: | ||
| 803 | controller.battery_state.dual = { | ||
| 804 | .is_powered = is_powered, | ||
| 805 | .is_charging = is_charging, | ||
| 806 | .battery_level = battery_level, | ||
| 807 | }; | ||
| 808 | break; | ||
| 809 | } | ||
| 810 | TriggerOnChange(ControllerTriggerType::Battery, true); | ||
| 811 | } | ||
| 812 | |||
| 813 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { | ||
| 814 | if (device_index >= output_devices.size()) { | ||
| 815 | return false; | ||
| 816 | } | ||
| 817 | if (!output_devices[device_index]) { | ||
| 818 | return false; | ||
| 819 | } | ||
| 820 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 821 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 822 | const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; | ||
| 823 | |||
| 824 | if (!player.vibration_enabled) { | ||
| 825 | return false; | ||
| 826 | } | ||
| 827 | |||
| 828 | // Exponential amplification is too strong at low amplitudes. Switch to a linear | ||
| 829 | // amplification if strength is set below 0.7f | ||
| 830 | const Common::Input::VibrationAmplificationType type = | ||
| 831 | strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential | ||
| 832 | : Common::Input::VibrationAmplificationType::Linear; | ||
| 833 | |||
| 834 | const Common::Input::VibrationStatus status = { | ||
| 835 | .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f), | ||
| 836 | .low_frequency = vibration.low_frequency, | ||
| 837 | .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f), | ||
| 838 | .high_frequency = vibration.high_frequency, | ||
| 839 | .type = type, | ||
| 840 | }; | ||
| 841 | return output_devices[device_index]->SetVibration(status) == | ||
| 842 | Common::Input::VibrationError::None; | ||
| 843 | } | ||
| 844 | |||
| 845 | bool EmulatedController::TestVibration(std::size_t device_index) { | ||
| 846 | if (device_index >= output_devices.size()) { | ||
| 847 | return false; | ||
| 848 | } | ||
| 849 | if (!output_devices[device_index]) { | ||
| 850 | return false; | ||
| 851 | } | ||
| 852 | |||
| 853 | // Send a slight vibration to test for rumble support | ||
| 854 | constexpr Common::Input::VibrationStatus status = { | ||
| 855 | .low_amplitude = 0.001f, | ||
| 856 | .low_frequency = 160.0f, | ||
| 857 | .high_amplitude = 0.001f, | ||
| 858 | .high_frequency = 320.0f, | ||
| 859 | .type = Common::Input::VibrationAmplificationType::Linear, | ||
| 860 | }; | ||
| 861 | return output_devices[device_index]->SetVibration(status) == | ||
| 862 | Common::Input::VibrationError::None; | ||
| 863 | } | ||
| 864 | |||
| 865 | void EmulatedController::SetLedPattern() { | ||
| 866 | for (auto& device : output_devices) { | ||
| 867 | if (!device) { | ||
| 868 | continue; | ||
| 869 | } | ||
| 870 | |||
| 871 | const LedPattern pattern = GetLedPattern(); | ||
| 872 | const Common::Input::LedStatus status = { | ||
| 873 | .led_1 = pattern.position1 != 0, | ||
| 874 | .led_2 = pattern.position2 != 0, | ||
| 875 | .led_3 = pattern.position3 != 0, | ||
| 876 | .led_4 = pattern.position4 != 0, | ||
| 877 | }; | ||
| 878 | device->SetLED(status); | ||
| 879 | } | ||
| 880 | } | ||
| 881 | |||
| 882 | void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) { | ||
| 883 | supported_style_tag = supported_styles; | ||
| 884 | if (!is_connected) { | ||
| 885 | return; | ||
| 886 | } | ||
| 887 | if (!IsControllerSupported()) { | ||
| 888 | LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", | ||
| 889 | npad_type); | ||
| 890 | Disconnect(); | ||
| 891 | } | ||
| 892 | } | ||
| 893 | |||
| 894 | bool EmulatedController::IsControllerSupported() const { | ||
| 895 | switch (npad_type) { | ||
| 896 | case NpadStyleIndex::ProController: | ||
| 897 | return supported_style_tag.fullkey; | ||
| 898 | case NpadStyleIndex::Handheld: | ||
| 899 | return supported_style_tag.handheld; | ||
| 900 | case NpadStyleIndex::JoyconDual: | ||
| 901 | return supported_style_tag.joycon_dual; | ||
| 902 | case NpadStyleIndex::JoyconLeft: | ||
| 903 | return supported_style_tag.joycon_left; | ||
| 904 | case NpadStyleIndex::JoyconRight: | ||
| 905 | return supported_style_tag.joycon_right; | ||
| 906 | case NpadStyleIndex::GameCube: | ||
| 907 | return supported_style_tag.gamecube; | ||
| 908 | case NpadStyleIndex::Pokeball: | ||
| 909 | return supported_style_tag.palma; | ||
| 910 | case NpadStyleIndex::NES: | ||
| 911 | return supported_style_tag.lark; | ||
| 912 | case NpadStyleIndex::SNES: | ||
| 913 | return supported_style_tag.lucia; | ||
| 914 | case NpadStyleIndex::N64: | ||
| 915 | return supported_style_tag.lagoon; | ||
| 916 | case NpadStyleIndex::SegaGenesis: | ||
| 917 | return supported_style_tag.lager; | ||
| 918 | default: | ||
| 919 | return false; | ||
| 920 | } | ||
| 921 | } | ||
| 922 | |||
| 923 | void EmulatedController::Connect() { | ||
| 924 | if (!IsControllerSupported()) { | ||
| 925 | LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type); | ||
| 926 | return; | ||
| 927 | } | ||
| 928 | { | ||
| 929 | std::lock_guard lock{mutex}; | ||
| 930 | if (is_configuring) { | ||
| 931 | tmp_is_connected = true; | ||
| 932 | TriggerOnChange(ControllerTriggerType::Connected, false); | ||
| 933 | return; | ||
| 934 | } | ||
| 935 | |||
| 936 | if (is_connected) { | ||
| 937 | return; | ||
| 938 | } | ||
| 939 | is_connected = true; | ||
| 940 | } | ||
| 941 | TriggerOnChange(ControllerTriggerType::Connected, true); | ||
| 942 | } | ||
| 943 | |||
| 944 | void EmulatedController::Disconnect() { | ||
| 945 | { | ||
| 946 | std::lock_guard lock{mutex}; | ||
| 947 | if (is_configuring) { | ||
| 948 | tmp_is_connected = false; | ||
| 949 | TriggerOnChange(ControllerTriggerType::Disconnected, false); | ||
| 950 | return; | ||
| 951 | } | ||
| 952 | |||
| 953 | if (!is_connected) { | ||
| 954 | return; | ||
| 955 | } | ||
| 956 | is_connected = false; | ||
| 957 | } | ||
| 958 | TriggerOnChange(ControllerTriggerType::Disconnected, true); | ||
| 959 | } | ||
| 960 | |||
| 961 | bool EmulatedController::IsConnected(bool get_temporary_value) const { | ||
| 962 | if (get_temporary_value && is_configuring) { | ||
| 963 | return tmp_is_connected; | ||
| 964 | } | ||
| 965 | return is_connected; | ||
| 966 | } | ||
| 967 | |||
| 968 | bool EmulatedController::IsVibrationEnabled() const { | ||
| 969 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 970 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 971 | return player.vibration_enabled; | ||
| 972 | } | ||
| 973 | |||
| 974 | NpadIdType EmulatedController::GetNpadIdType() const { | ||
| 975 | return npad_id_type; | ||
| 976 | } | ||
| 977 | |||
| 978 | NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { | ||
| 979 | if (get_temporary_value && is_configuring) { | ||
| 980 | return tmp_npad_type; | ||
| 981 | } | ||
| 982 | return npad_type; | ||
| 983 | } | ||
| 984 | |||
| 985 | void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { | ||
| 986 | { | ||
| 987 | std::lock_guard lock{mutex}; | ||
| 988 | |||
| 989 | if (is_configuring) { | ||
| 990 | if (tmp_npad_type == npad_type_) { | ||
| 991 | return; | ||
| 992 | } | ||
| 993 | tmp_npad_type = npad_type_; | ||
| 994 | TriggerOnChange(ControllerTriggerType::Type, false); | ||
| 995 | return; | ||
| 996 | } | ||
| 997 | |||
| 998 | if (npad_type == npad_type_) { | ||
| 999 | return; | ||
| 1000 | } | ||
| 1001 | if (is_connected) { | ||
| 1002 | LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", | ||
| 1003 | NpadIdTypeToIndex(npad_id_type)); | ||
| 1004 | } | ||
| 1005 | npad_type = npad_type_; | ||
| 1006 | } | ||
| 1007 | TriggerOnChange(ControllerTriggerType::Type, true); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | LedPattern EmulatedController::GetLedPattern() const { | ||
| 1011 | switch (npad_id_type) { | ||
| 1012 | case NpadIdType::Player1: | ||
| 1013 | return LedPattern{1, 0, 0, 0}; | ||
| 1014 | case NpadIdType::Player2: | ||
| 1015 | return LedPattern{1, 1, 0, 0}; | ||
| 1016 | case NpadIdType::Player3: | ||
| 1017 | return LedPattern{1, 1, 1, 0}; | ||
| 1018 | case NpadIdType::Player4: | ||
| 1019 | return LedPattern{1, 1, 1, 1}; | ||
| 1020 | case NpadIdType::Player5: | ||
| 1021 | return LedPattern{1, 0, 0, 1}; | ||
| 1022 | case NpadIdType::Player6: | ||
| 1023 | return LedPattern{1, 0, 1, 0}; | ||
| 1024 | case NpadIdType::Player7: | ||
| 1025 | return LedPattern{1, 0, 1, 1}; | ||
| 1026 | case NpadIdType::Player8: | ||
| 1027 | return LedPattern{0, 1, 1, 0}; | ||
| 1028 | default: | ||
| 1029 | return LedPattern{0, 0, 0, 0}; | ||
| 1030 | } | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | ButtonValues EmulatedController::GetButtonsValues() const { | ||
| 1034 | return controller.button_values; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | SticksValues EmulatedController::GetSticksValues() const { | ||
| 1038 | return controller.stick_values; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | TriggerValues EmulatedController::GetTriggersValues() const { | ||
| 1042 | return controller.trigger_values; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | ControllerMotionValues EmulatedController::GetMotionValues() const { | ||
| 1046 | return controller.motion_values; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | ColorValues EmulatedController::GetColorsValues() const { | ||
| 1050 | return controller.color_values; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | BatteryValues EmulatedController::GetBatteryValues() const { | ||
| 1054 | return controller.battery_values; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | NpadButtonState EmulatedController::GetNpadButtons() const { | ||
| 1058 | if (is_configuring) { | ||
| 1059 | return {}; | ||
| 1060 | } | ||
| 1061 | return controller.npad_button_state; | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | DebugPadButton EmulatedController::GetDebugPadButtons() const { | ||
| 1065 | if (is_configuring) { | ||
| 1066 | return {}; | ||
| 1067 | } | ||
| 1068 | return controller.debug_pad_button_state; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | AnalogSticks EmulatedController::GetSticks() const { | ||
| 1072 | if (is_configuring) { | ||
| 1073 | return {}; | ||
| 1074 | } | ||
| 1075 | // Some drivers like stick from buttons need constant refreshing | ||
| 1076 | for (auto& device : stick_devices) { | ||
| 1077 | if (!device) { | ||
| 1078 | continue; | ||
| 1079 | } | ||
| 1080 | device->SoftUpdate(); | ||
| 1081 | } | ||
| 1082 | return controller.analog_stick_state; | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | NpadGcTriggerState EmulatedController::GetTriggers() const { | ||
| 1086 | if (is_configuring) { | ||
| 1087 | return {}; | ||
| 1088 | } | ||
| 1089 | return controller.gc_trigger_state; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | MotionState EmulatedController::GetMotions() const { | ||
| 1093 | if (force_update_motion) { | ||
| 1094 | for (auto& device : motion_devices) { | ||
| 1095 | if (!device) { | ||
| 1096 | continue; | ||
| 1097 | } | ||
| 1098 | device->ForceUpdate(); | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | return controller.motion_state; | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | ControllerColors EmulatedController::GetColors() const { | ||
| 1105 | return controller.colors_state; | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | BatteryLevelState EmulatedController::GetBattery() const { | ||
| 1109 | return controller.battery_state; | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { | ||
| 1113 | for (const auto& poller_pair : callback_list) { | ||
| 1114 | const ControllerUpdateCallback& poller = poller_pair.second; | ||
| 1115 | if (!is_npad_service_update && poller.is_npad_service) { | ||
| 1116 | continue; | ||
| 1117 | } | ||
| 1118 | if (poller.on_change) { | ||
| 1119 | poller.on_change(type); | ||
| 1120 | } | ||
| 1121 | } | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { | ||
| 1125 | std::lock_guard lock{mutex}; | ||
| 1126 | callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); | ||
| 1127 | return last_callback_key++; | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | void EmulatedController::DeleteCallback(int key) { | ||
| 1131 | std::lock_guard lock{mutex}; | ||
| 1132 | const auto& iterator = callback_list.find(key); | ||
| 1133 | if (iterator == callback_list.end()) { | ||
| 1134 | LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); | ||
| 1135 | return; | ||
| 1136 | } | ||
| 1137 | callback_list.erase(iterator); | ||
| 1138 | } | ||
| 1139 | } // namespace Core::HID | ||
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h new file mode 100644 index 000000000..e42aafebc --- /dev/null +++ b/src/core/hid/emulated_controller.h | |||
| @@ -0,0 +1,411 @@ | |||
| 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 (buttons, joysticks, vibration, and motion) within this controller. | ||
| 136 | * @param npad_id_type 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 get_temporary_value 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 | /** | ||
| 164 | * Sets the supported controller types. Disconnects the controller if current type is not | ||
| 165 | * supported | ||
| 166 | * @param supported_styles bitflag with supported types | ||
| 167 | */ | ||
| 168 | void SetSupportedNpadStyleTag(NpadStyleTag supported_styles); | ||
| 169 | |||
| 170 | /// Sets the connected status to true | ||
| 171 | void Connect(); | ||
| 172 | |||
| 173 | /// Sets the connected status to false | ||
| 174 | void Disconnect(); | ||
| 175 | |||
| 176 | /** | ||
| 177 | * Is the emulated connected | ||
| 178 | * @param get_temporary_value If true tmp_is_connected will be returned | ||
| 179 | * @return true if the controller has the connected status | ||
| 180 | */ | ||
| 181 | bool IsConnected(bool get_temporary_value = false) const; | ||
| 182 | |||
| 183 | /// Returns true if vibration is enabled | ||
| 184 | bool IsVibrationEnabled() const; | ||
| 185 | |||
| 186 | /// Removes all callbacks created from input devices | ||
| 187 | void UnloadInput(); | ||
| 188 | |||
| 189 | /** | ||
| 190 | * Sets the emulated controller into configuring mode | ||
| 191 | * This prevents the modification of the HID state of the emulated controller by input commands | ||
| 192 | */ | ||
| 193 | void EnableConfiguration(); | ||
| 194 | |||
| 195 | /// Returns the emulated controller into normal mode, allowing the modification of the HID state | ||
| 196 | void DisableConfiguration(); | ||
| 197 | |||
| 198 | /// Returns true if the emulated controller is in configuring mode | ||
| 199 | bool IsConfiguring() const; | ||
| 200 | |||
| 201 | /// Reload all input devices | ||
| 202 | void ReloadInput(); | ||
| 203 | |||
| 204 | /// Overrides current mapped devices with the stored configuration and reloads all input devices | ||
| 205 | void ReloadFromSettings(); | ||
| 206 | |||
| 207 | /// Saves the current mapped configuration | ||
| 208 | void SaveCurrentConfig(); | ||
| 209 | |||
| 210 | /// Reverts any mapped changes made that weren't saved | ||
| 211 | void RestoreConfig(); | ||
| 212 | |||
| 213 | /// Returns a vector of mapped devices from the mapped button and stick parameters | ||
| 214 | std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const; | ||
| 215 | |||
| 216 | // Returns the current mapped button device | ||
| 217 | Common::ParamPackage GetButtonParam(std::size_t index) const; | ||
| 218 | |||
| 219 | // Returns the current mapped stick device | ||
| 220 | Common::ParamPackage GetStickParam(std::size_t index) const; | ||
| 221 | |||
| 222 | // Returns the current mapped motion device | ||
| 223 | Common::ParamPackage GetMotionParam(std::size_t index) const; | ||
| 224 | |||
| 225 | /** | ||
| 226 | * Updates the current mapped button device | ||
| 227 | * @param param ParamPackage with controller data to be mapped | ||
| 228 | */ | ||
| 229 | void SetButtonParam(std::size_t index, Common::ParamPackage param); | ||
| 230 | |||
| 231 | /** | ||
| 232 | * Updates the current mapped stick device | ||
| 233 | * @param param ParamPackage with controller data to be mapped | ||
| 234 | */ | ||
| 235 | void SetStickParam(std::size_t index, Common::ParamPackage param); | ||
| 236 | |||
| 237 | /** | ||
| 238 | * Updates the current mapped motion device | ||
| 239 | * @param param ParamPackage with controller data to be mapped | ||
| 240 | */ | ||
| 241 | void SetMotionParam(std::size_t index, Common::ParamPackage param); | ||
| 242 | |||
| 243 | /// Returns the latest button status from the controller with parameters | ||
| 244 | ButtonValues GetButtonsValues() const; | ||
| 245 | |||
| 246 | /// Returns the latest analog stick status from the controller with parameters | ||
| 247 | SticksValues GetSticksValues() const; | ||
| 248 | |||
| 249 | /// Returns the latest trigger status from the controller with parameters | ||
| 250 | TriggerValues GetTriggersValues() const; | ||
| 251 | |||
| 252 | /// Returns the latest motion status from the controller with parameters | ||
| 253 | ControllerMotionValues GetMotionValues() const; | ||
| 254 | |||
| 255 | /// Returns the latest color status from the controller with parameters | ||
| 256 | ColorValues GetColorsValues() const; | ||
| 257 | |||
| 258 | /// Returns the latest battery status from the controller with parameters | ||
| 259 | BatteryValues GetBatteryValues() const; | ||
| 260 | |||
| 261 | /// Returns the latest status of button input for the npad service | ||
| 262 | NpadButtonState GetNpadButtons() const; | ||
| 263 | |||
| 264 | /// Returns the latest status of button input for the debug pad service | ||
| 265 | DebugPadButton GetDebugPadButtons() const; | ||
| 266 | |||
| 267 | /// Returns the latest status of stick input from the mouse | ||
| 268 | AnalogSticks GetSticks() const; | ||
| 269 | |||
| 270 | /// Returns the latest status of trigger input from the mouse | ||
| 271 | NpadGcTriggerState GetTriggers() const; | ||
| 272 | |||
| 273 | /// Returns the latest status of motion input from the mouse | ||
| 274 | MotionState GetMotions() const; | ||
| 275 | |||
| 276 | /// Returns the latest color value from the controller | ||
| 277 | ControllerColors GetColors() const; | ||
| 278 | |||
| 279 | /// Returns the latest battery status from the controller | ||
| 280 | BatteryLevelState GetBattery() const; | ||
| 281 | |||
| 282 | /** | ||
| 283 | * Sends a specific vibration to the output device | ||
| 284 | * @return returns true if vibration had no errors | ||
| 285 | */ | ||
| 286 | bool SetVibration(std::size_t device_index, VibrationValue vibration); | ||
| 287 | |||
| 288 | /** | ||
| 289 | * Sends a small vibration to the output device | ||
| 290 | * @return returns true if SetVibration was successfull | ||
| 291 | */ | ||
| 292 | bool TestVibration(std::size_t device_index); | ||
| 293 | |||
| 294 | /// Returns the led pattern corresponding to this emulated controller | ||
| 295 | LedPattern GetLedPattern() const; | ||
| 296 | |||
| 297 | /// Asks the output device to change the player led pattern | ||
| 298 | void SetLedPattern(); | ||
| 299 | |||
| 300 | /** | ||
| 301 | * Adds a callback to the list of events | ||
| 302 | * @param update_callback A ConsoleUpdateCallback that will be triggered | ||
| 303 | * @return an unique key corresponding to the callback index in the list | ||
| 304 | */ | ||
| 305 | int SetCallback(ControllerUpdateCallback update_callback); | ||
| 306 | |||
| 307 | /** | ||
| 308 | * Removes a callback from the list stopping any future events to this object | ||
| 309 | * @param key Key corresponding to the callback index in the list | ||
| 310 | */ | ||
| 311 | void DeleteCallback(int key); | ||
| 312 | |||
| 313 | private: | ||
| 314 | /// creates input devices from params | ||
| 315 | void LoadDevices(); | ||
| 316 | |||
| 317 | /// Set the params for TAS devices | ||
| 318 | void LoadTASParams(); | ||
| 319 | |||
| 320 | /** | ||
| 321 | * Checks the current controller type against the supported_style_tag | ||
| 322 | * @return true if the controller is supported | ||
| 323 | */ | ||
| 324 | bool IsControllerSupported() const; | ||
| 325 | |||
| 326 | /** | ||
| 327 | * Updates the button status of the controller | ||
| 328 | * @param callback A CallbackStatus containing the button status | ||
| 329 | * @param index Button ID of the to be updated | ||
| 330 | */ | ||
| 331 | void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, | ||
| 332 | Common::UUID uuid); | ||
| 333 | |||
| 334 | /** | ||
| 335 | * Updates the analog stick status of the controller | ||
| 336 | * @param callback A CallbackStatus containing the analog stick status | ||
| 337 | * @param index stick ID of the to be updated | ||
| 338 | */ | ||
| 339 | void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index, | ||
| 340 | Common::UUID uuid); | ||
| 341 | |||
| 342 | /** | ||
| 343 | * Updates the trigger status of the controller | ||
| 344 | * @param callback A CallbackStatus containing the trigger status | ||
| 345 | * @param index trigger ID of the to be updated | ||
| 346 | */ | ||
| 347 | void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index, | ||
| 348 | Common::UUID uuid); | ||
| 349 | |||
| 350 | /** | ||
| 351 | * Updates the motion status of the controller | ||
| 352 | * @param callback A CallbackStatus containing gyro and accelerometer data | ||
| 353 | * @param index motion ID of the to be updated | ||
| 354 | */ | ||
| 355 | void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); | ||
| 356 | |||
| 357 | /** | ||
| 358 | * Updates the battery status of the controller | ||
| 359 | * @param callback A CallbackStatus containing the battery status | ||
| 360 | * @param index Button ID of the to be updated | ||
| 361 | */ | ||
| 362 | void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); | ||
| 363 | |||
| 364 | /** | ||
| 365 | * Triggers a callback that something has changed on the controller status | ||
| 366 | * @param type Input type of the event to trigger | ||
| 367 | * @param is_service_update indicates if this event should only be sent to HID services | ||
| 368 | */ | ||
| 369 | void TriggerOnChange(ControllerTriggerType type, bool is_service_update); | ||
| 370 | |||
| 371 | NpadIdType npad_id_type; | ||
| 372 | NpadStyleIndex npad_type{NpadStyleIndex::None}; | ||
| 373 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; | ||
| 374 | bool is_connected{false}; | ||
| 375 | bool is_configuring{false}; | ||
| 376 | f32 motion_sensitivity{0.01f}; | ||
| 377 | bool force_update_motion{false}; | ||
| 378 | |||
| 379 | // Temporary values to avoid doing changes while the controller is in configuring mode | ||
| 380 | NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; | ||
| 381 | bool tmp_is_connected{false}; | ||
| 382 | |||
| 383 | ButtonParams button_params; | ||
| 384 | StickParams stick_params; | ||
| 385 | ControllerMotionParams motion_params; | ||
| 386 | TriggerParams trigger_params; | ||
| 387 | BatteryParams battery_params; | ||
| 388 | OutputParams output_params; | ||
| 389 | |||
| 390 | ButtonDevices button_devices; | ||
| 391 | StickDevices stick_devices; | ||
| 392 | ControllerMotionDevices motion_devices; | ||
| 393 | TriggerDevices trigger_devices; | ||
| 394 | BatteryDevices battery_devices; | ||
| 395 | OutputDevices output_devices; | ||
| 396 | |||
| 397 | // TAS related variables | ||
| 398 | ButtonParams tas_button_params; | ||
| 399 | StickParams tas_stick_params; | ||
| 400 | ButtonDevices tas_button_devices; | ||
| 401 | StickDevices tas_stick_devices; | ||
| 402 | |||
| 403 | mutable std::mutex mutex; | ||
| 404 | std::unordered_map<int, ControllerUpdateCallback> callback_list; | ||
| 405 | int last_callback_key = 0; | ||
| 406 | |||
| 407 | // Stores the current status of all controller input | ||
| 408 | ControllerStatus controller; | ||
| 409 | }; | ||
| 410 | |||
| 411 | } // 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..708480f2d --- /dev/null +++ b/src/core/hid/emulated_devices.cpp | |||
| @@ -0,0 +1,459 @@ | |||
| 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 | mouse_button_devices[index]->SetCallback({ | ||
| 74 | .on_change = | ||
| 75 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 76 | SetMouseButton(callback, index); | ||
| 77 | }, | ||
| 78 | }); | ||
| 79 | } | ||
| 80 | |||
| 81 | for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) { | ||
| 82 | if (!mouse_analog_devices[index]) { | ||
| 83 | continue; | ||
| 84 | } | ||
| 85 | mouse_analog_devices[index]->SetCallback({ | ||
| 86 | .on_change = | ||
| 87 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 88 | SetMouseAnalog(callback, index); | ||
| 89 | }, | ||
| 90 | }); | ||
| 91 | } | ||
| 92 | |||
| 93 | if (mouse_stick_device) { | ||
| 94 | mouse_stick_device->SetCallback({ | ||
| 95 | .on_change = | ||
| 96 | [this](const Common::Input::CallbackStatus& callback) { SetMouseStick(callback); }, | ||
| 97 | }); | ||
| 98 | } | ||
| 99 | |||
| 100 | for (std::size_t index = 0; index < keyboard_devices.size(); ++index) { | ||
| 101 | if (!keyboard_devices[index]) { | ||
| 102 | continue; | ||
| 103 | } | ||
| 104 | keyboard_devices[index]->SetCallback({ | ||
| 105 | .on_change = | ||
| 106 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 107 | SetKeyboardButton(callback, index); | ||
| 108 | }, | ||
| 109 | }); | ||
| 110 | } | ||
| 111 | |||
| 112 | for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) { | ||
| 113 | if (!keyboard_modifier_devices[index]) { | ||
| 114 | continue; | ||
| 115 | } | ||
| 116 | keyboard_modifier_devices[index]->SetCallback({ | ||
| 117 | .on_change = | ||
| 118 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 119 | SetKeyboardModifier(callback, index); | ||
| 120 | }, | ||
| 121 | }); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | void EmulatedDevices::UnloadInput() { | ||
| 126 | for (auto& button : mouse_button_devices) { | ||
| 127 | button.reset(); | ||
| 128 | } | ||
| 129 | for (auto& analog : mouse_analog_devices) { | ||
| 130 | analog.reset(); | ||
| 131 | } | ||
| 132 | mouse_stick_device.reset(); | ||
| 133 | for (auto& button : keyboard_devices) { | ||
| 134 | button.reset(); | ||
| 135 | } | ||
| 136 | for (auto& button : keyboard_modifier_devices) { | ||
| 137 | button.reset(); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | void EmulatedDevices::EnableConfiguration() { | ||
| 142 | is_configuring = true; | ||
| 143 | SaveCurrentConfig(); | ||
| 144 | } | ||
| 145 | |||
| 146 | void EmulatedDevices::DisableConfiguration() { | ||
| 147 | is_configuring = false; | ||
| 148 | } | ||
| 149 | |||
| 150 | bool EmulatedDevices::IsConfiguring() const { | ||
| 151 | return is_configuring; | ||
| 152 | } | ||
| 153 | |||
| 154 | void EmulatedDevices::SaveCurrentConfig() { | ||
| 155 | if (!is_configuring) { | ||
| 156 | return; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | void EmulatedDevices::RestoreConfig() { | ||
| 161 | if (!is_configuring) { | ||
| 162 | return; | ||
| 163 | } | ||
| 164 | ReloadFromSettings(); | ||
| 165 | } | ||
| 166 | |||
| 167 | void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, | ||
| 168 | std::size_t index) { | ||
| 169 | if (index >= device_status.keyboard_values.size()) { | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | std::lock_guard lock{mutex}; | ||
| 173 | bool value_changed = false; | ||
| 174 | const auto new_status = TransformToButton(callback); | ||
| 175 | auto& current_status = device_status.keyboard_values[index]; | ||
| 176 | current_status.toggle = new_status.toggle; | ||
| 177 | |||
| 178 | // Update button status with current status | ||
| 179 | if (!current_status.toggle) { | ||
| 180 | current_status.locked = false; | ||
| 181 | if (current_status.value != new_status.value) { | ||
| 182 | current_status.value = new_status.value; | ||
| 183 | value_changed = true; | ||
| 184 | } | ||
| 185 | } else { | ||
| 186 | // Toggle button and lock status | ||
| 187 | if (new_status.value && !current_status.locked) { | ||
| 188 | current_status.locked = true; | ||
| 189 | current_status.value = !current_status.value; | ||
| 190 | value_changed = true; | ||
| 191 | } | ||
| 192 | |||
| 193 | // Unlock button, ready for next press | ||
| 194 | if (!new_status.value && current_status.locked) { | ||
| 195 | current_status.locked = false; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | if (!value_changed) { | ||
| 200 | return; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (is_configuring) { | ||
| 204 | TriggerOnChange(DeviceTriggerType::Keyboard); | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | |||
| 208 | // Index should be converted from NativeKeyboard to KeyboardKeyIndex | ||
| 209 | UpdateKey(index, current_status.value); | ||
| 210 | |||
| 211 | TriggerOnChange(DeviceTriggerType::Keyboard); | ||
| 212 | } | ||
| 213 | |||
| 214 | void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) { | ||
| 215 | constexpr std::size_t KEYS_PER_BYTE = 8; | ||
| 216 | auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE]; | ||
| 217 | const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE)); | ||
| 218 | if (status) { | ||
| 219 | entry = entry | mask; | ||
| 220 | } else { | ||
| 221 | entry = static_cast<u8>(entry & ~mask); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback, | ||
| 226 | std::size_t index) { | ||
| 227 | if (index >= device_status.keyboard_moddifier_values.size()) { | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | std::lock_guard lock{mutex}; | ||
| 231 | bool value_changed = false; | ||
| 232 | const auto new_status = TransformToButton(callback); | ||
| 233 | auto& current_status = device_status.keyboard_moddifier_values[index]; | ||
| 234 | current_status.toggle = new_status.toggle; | ||
| 235 | |||
| 236 | // Update button status with current | ||
| 237 | if (!current_status.toggle) { | ||
| 238 | current_status.locked = false; | ||
| 239 | if (current_status.value != new_status.value) { | ||
| 240 | current_status.value = new_status.value; | ||
| 241 | value_changed = true; | ||
| 242 | } | ||
| 243 | } else { | ||
| 244 | // Toggle button and lock status | ||
| 245 | if (new_status.value && !current_status.locked) { | ||
| 246 | current_status.locked = true; | ||
| 247 | current_status.value = !current_status.value; | ||
| 248 | value_changed = true; | ||
| 249 | } | ||
| 250 | |||
| 251 | // Unlock button ready for next press | ||
| 252 | if (!new_status.value && current_status.locked) { | ||
| 253 | current_status.locked = false; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | if (!value_changed) { | ||
| 258 | return; | ||
| 259 | } | ||
| 260 | |||
| 261 | if (is_configuring) { | ||
| 262 | TriggerOnChange(DeviceTriggerType::KeyboardModdifier); | ||
| 263 | return; | ||
| 264 | } | ||
| 265 | |||
| 266 | switch (index) { | ||
| 267 | case Settings::NativeKeyboard::LeftControl: | ||
| 268 | case Settings::NativeKeyboard::RightControl: | ||
| 269 | device_status.keyboard_moddifier_state.control.Assign(current_status.value); | ||
| 270 | break; | ||
| 271 | case Settings::NativeKeyboard::LeftShift: | ||
| 272 | case Settings::NativeKeyboard::RightShift: | ||
| 273 | device_status.keyboard_moddifier_state.shift.Assign(current_status.value); | ||
| 274 | break; | ||
| 275 | case Settings::NativeKeyboard::LeftAlt: | ||
| 276 | device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value); | ||
| 277 | break; | ||
| 278 | case Settings::NativeKeyboard::RightAlt: | ||
| 279 | device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value); | ||
| 280 | break; | ||
| 281 | case Settings::NativeKeyboard::CapsLock: | ||
| 282 | device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value); | ||
| 283 | break; | ||
| 284 | case Settings::NativeKeyboard::ScrollLock: | ||
| 285 | device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value); | ||
| 286 | break; | ||
| 287 | case Settings::NativeKeyboard::NumLock: | ||
| 288 | device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value); | ||
| 289 | break; | ||
| 290 | } | ||
| 291 | |||
| 292 | TriggerOnChange(DeviceTriggerType::KeyboardModdifier); | ||
| 293 | } | ||
| 294 | |||
| 295 | void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback, | ||
| 296 | std::size_t index) { | ||
| 297 | if (index >= device_status.mouse_button_values.size()) { | ||
| 298 | return; | ||
| 299 | } | ||
| 300 | std::lock_guard lock{mutex}; | ||
| 301 | bool value_changed = false; | ||
| 302 | const auto new_status = TransformToButton(callback); | ||
| 303 | auto& current_status = device_status.mouse_button_values[index]; | ||
| 304 | current_status.toggle = new_status.toggle; | ||
| 305 | |||
| 306 | // Update button status with current | ||
| 307 | if (!current_status.toggle) { | ||
| 308 | current_status.locked = false; | ||
| 309 | if (current_status.value != new_status.value) { | ||
| 310 | current_status.value = new_status.value; | ||
| 311 | value_changed = true; | ||
| 312 | } | ||
| 313 | } else { | ||
| 314 | // Toggle button and lock status | ||
| 315 | if (new_status.value && !current_status.locked) { | ||
| 316 | current_status.locked = true; | ||
| 317 | current_status.value = !current_status.value; | ||
| 318 | value_changed = true; | ||
| 319 | } | ||
| 320 | |||
| 321 | // Unlock button ready for next press | ||
| 322 | if (!new_status.value && current_status.locked) { | ||
| 323 | current_status.locked = false; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | if (!value_changed) { | ||
| 328 | return; | ||
| 329 | } | ||
| 330 | |||
| 331 | if (is_configuring) { | ||
| 332 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 333 | return; | ||
| 334 | } | ||
| 335 | |||
| 336 | switch (index) { | ||
| 337 | case Settings::NativeMouseButton::Left: | ||
| 338 | device_status.mouse_button_state.left.Assign(current_status.value); | ||
| 339 | break; | ||
| 340 | case Settings::NativeMouseButton::Right: | ||
| 341 | device_status.mouse_button_state.right.Assign(current_status.value); | ||
| 342 | break; | ||
| 343 | case Settings::NativeMouseButton::Middle: | ||
| 344 | device_status.mouse_button_state.middle.Assign(current_status.value); | ||
| 345 | break; | ||
| 346 | case Settings::NativeMouseButton::Forward: | ||
| 347 | device_status.mouse_button_state.forward.Assign(current_status.value); | ||
| 348 | break; | ||
| 349 | case Settings::NativeMouseButton::Back: | ||
| 350 | device_status.mouse_button_state.back.Assign(current_status.value); | ||
| 351 | break; | ||
| 352 | } | ||
| 353 | |||
| 354 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 355 | } | ||
| 356 | |||
| 357 | void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callback, | ||
| 358 | std::size_t index) { | ||
| 359 | if (index >= device_status.mouse_analog_values.size()) { | ||
| 360 | return; | ||
| 361 | } | ||
| 362 | std::lock_guard lock{mutex}; | ||
| 363 | const auto analog_value = TransformToAnalog(callback); | ||
| 364 | |||
| 365 | device_status.mouse_analog_values[index] = analog_value; | ||
| 366 | |||
| 367 | if (is_configuring) { | ||
| 368 | device_status.mouse_position_state = {}; | ||
| 369 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 370 | return; | ||
| 371 | } | ||
| 372 | |||
| 373 | switch (index) { | ||
| 374 | case Settings::NativeMouseWheel::X: | ||
| 375 | device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value); | ||
| 376 | break; | ||
| 377 | case Settings::NativeMouseWheel::Y: | ||
| 378 | device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value); | ||
| 379 | break; | ||
| 380 | } | ||
| 381 | |||
| 382 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 383 | } | ||
| 384 | |||
| 385 | void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) { | ||
| 386 | std::lock_guard lock{mutex}; | ||
| 387 | const auto touch_value = TransformToTouch(callback); | ||
| 388 | |||
| 389 | device_status.mouse_stick_value = touch_value; | ||
| 390 | |||
| 391 | if (is_configuring) { | ||
| 392 | device_status.mouse_position_state = {}; | ||
| 393 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 394 | return; | ||
| 395 | } | ||
| 396 | |||
| 397 | device_status.mouse_position_state.x = touch_value.x.value; | ||
| 398 | device_status.mouse_position_state.y = touch_value.y.value; | ||
| 399 | |||
| 400 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 401 | } | ||
| 402 | |||
| 403 | KeyboardValues EmulatedDevices::GetKeyboardValues() const { | ||
| 404 | return device_status.keyboard_values; | ||
| 405 | } | ||
| 406 | |||
| 407 | KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { | ||
| 408 | return device_status.keyboard_moddifier_values; | ||
| 409 | } | ||
| 410 | |||
| 411 | MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { | ||
| 412 | return device_status.mouse_button_values; | ||
| 413 | } | ||
| 414 | |||
| 415 | KeyboardKey EmulatedDevices::GetKeyboard() const { | ||
| 416 | return device_status.keyboard_state; | ||
| 417 | } | ||
| 418 | |||
| 419 | KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { | ||
| 420 | return device_status.keyboard_moddifier_state; | ||
| 421 | } | ||
| 422 | |||
| 423 | MouseButton EmulatedDevices::GetMouseButtons() const { | ||
| 424 | return device_status.mouse_button_state; | ||
| 425 | } | ||
| 426 | |||
| 427 | MousePosition EmulatedDevices::GetMousePosition() const { | ||
| 428 | return device_status.mouse_position_state; | ||
| 429 | } | ||
| 430 | |||
| 431 | AnalogStickState EmulatedDevices::GetMouseWheel() const { | ||
| 432 | return device_status.mouse_wheel_state; | ||
| 433 | } | ||
| 434 | |||
| 435 | void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { | ||
| 436 | for (const auto& poller_pair : callback_list) { | ||
| 437 | const InterfaceUpdateCallback& poller = poller_pair.second; | ||
| 438 | if (poller.on_change) { | ||
| 439 | poller.on_change(type); | ||
| 440 | } | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { | ||
| 445 | std::lock_guard lock{mutex}; | ||
| 446 | callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); | ||
| 447 | return last_callback_key++; | ||
| 448 | } | ||
| 449 | |||
| 450 | void EmulatedDevices::DeleteCallback(int key) { | ||
| 451 | std::lock_guard lock{mutex}; | ||
| 452 | const auto& iterator = callback_list.find(key); | ||
| 453 | if (iterator == callback_list.end()) { | ||
| 454 | LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); | ||
| 455 | return; | ||
| 456 | } | ||
| 457 | callback_list.erase(iterator); | ||
| 458 | } | ||
| 459 | } // 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..790d3b411 --- /dev/null +++ b/src/core/hid/emulated_devices.h | |||
| @@ -0,0 +1,210 @@ | |||
| 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 | * This includes devices such as the keyboard or mouse | ||
| 79 | */ | ||
| 80 | explicit 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 | /** | ||
| 90 | * Sets the emulated devices into configuring mode | ||
| 91 | * This prevents the modification of the HID state of the emulated devices by input commands | ||
| 92 | */ | ||
| 93 | void EnableConfiguration(); | ||
| 94 | |||
| 95 | /// Returns the emulated devices into normal mode, allowing the modification of the HID state | ||
| 96 | void DisableConfiguration(); | ||
| 97 | |||
| 98 | /// Returns true if the emulated device is in configuring mode | ||
| 99 | bool IsConfiguring() const; | ||
| 100 | |||
| 101 | /// Reload all input devices | ||
| 102 | void ReloadInput(); | ||
| 103 | |||
| 104 | /// Overrides current mapped devices with the stored configuration and reloads all input devices | ||
| 105 | void ReloadFromSettings(); | ||
| 106 | |||
| 107 | /// Saves the current mapped configuration | ||
| 108 | void SaveCurrentConfig(); | ||
| 109 | |||
| 110 | /// Reverts any mapped changes made that weren't saved | ||
| 111 | void RestoreConfig(); | ||
| 112 | |||
| 113 | /// Returns the latest status of button input from the keyboard with parameters | ||
| 114 | KeyboardValues GetKeyboardValues() const; | ||
| 115 | |||
| 116 | /// Returns the latest status of button input from the keyboard modifiers with parameters | ||
| 117 | KeyboardModifierValues GetKeyboardModdifierValues() const; | ||
| 118 | |||
| 119 | /// Returns the latest status of button input from the mouse with parameters | ||
| 120 | MouseButtonValues GetMouseButtonsValues() const; | ||
| 121 | |||
| 122 | /// Returns the latest status of button input from the keyboard | ||
| 123 | KeyboardKey GetKeyboard() const; | ||
| 124 | |||
| 125 | /// Returns the latest status of button input from the keyboard modifiers | ||
| 126 | KeyboardModifier GetKeyboardModifier() const; | ||
| 127 | |||
| 128 | /// Returns the latest status of button input from the mouse | ||
| 129 | MouseButton GetMouseButtons() const; | ||
| 130 | |||
| 131 | /// Returns the latest mouse coordinates | ||
| 132 | MousePosition GetMousePosition() const; | ||
| 133 | |||
| 134 | /// Returns the latest mouse wheel change | ||
| 135 | AnalogStickState GetMouseWheel() const; | ||
| 136 | |||
| 137 | /** | ||
| 138 | * Adds a callback to the list of events | ||
| 139 | * @param update_callback InterfaceUpdateCallback that will be triggered | ||
| 140 | * @return an unique key corresponding to the callback index in the list | ||
| 141 | */ | ||
| 142 | int SetCallback(InterfaceUpdateCallback update_callback); | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Removes a callback from the list stopping any future events to this object | ||
| 146 | * @param key Key corresponding to the callback index in the list | ||
| 147 | */ | ||
| 148 | void DeleteCallback(int key); | ||
| 149 | |||
| 150 | private: | ||
| 151 | /// Helps assigning a value to keyboard_state | ||
| 152 | void UpdateKey(std::size_t key_index, bool status); | ||
| 153 | |||
| 154 | /** | ||
| 155 | * Updates the touch status of the keyboard device | ||
| 156 | * @param callback A CallbackStatus containing the key status | ||
| 157 | * @param index key ID to be updated | ||
| 158 | */ | ||
| 159 | void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index); | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Updates the keyboard status of the keyboard device | ||
| 163 | * @param callback A CallbackStatus containing the modifier key status | ||
| 164 | * @param index modifier key ID to be updated | ||
| 165 | */ | ||
| 166 | void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index); | ||
| 167 | |||
| 168 | /** | ||
| 169 | * Updates the mouse button status of the mouse device | ||
| 170 | * @param callback A CallbackStatus containing the button status | ||
| 171 | * @param index Button ID to be updated | ||
| 172 | */ | ||
| 173 | void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index); | ||
| 174 | |||
| 175 | /** | ||
| 176 | * Updates the mouse wheel status of the mouse device | ||
| 177 | * @param callback A CallbackStatus containing the wheel status | ||
| 178 | * @param index wheel ID to be updated | ||
| 179 | */ | ||
| 180 | void SetMouseAnalog(const Common::Input::CallbackStatus& callback, std::size_t index); | ||
| 181 | |||
| 182 | /** | ||
| 183 | * Updates the mouse position status of the mouse device | ||
| 184 | * @param callback A CallbackStatus containing the position status | ||
| 185 | */ | ||
| 186 | void SetMouseStick(const Common::Input::CallbackStatus& callback); | ||
| 187 | |||
| 188 | /** | ||
| 189 | * Triggers a callback that something has changed on the device status | ||
| 190 | * @param type Input type of the event to trigger | ||
| 191 | */ | ||
| 192 | void TriggerOnChange(DeviceTriggerType type); | ||
| 193 | |||
| 194 | bool is_configuring{false}; | ||
| 195 | |||
| 196 | KeyboardDevices keyboard_devices; | ||
| 197 | KeyboardModifierDevices keyboard_modifier_devices; | ||
| 198 | MouseButtonDevices mouse_button_devices; | ||
| 199 | MouseAnalogDevices mouse_analog_devices; | ||
| 200 | MouseStickDevice mouse_stick_device; | ||
| 201 | |||
| 202 | mutable std::mutex mutex; | ||
| 203 | std::unordered_map<int, InterfaceUpdateCallback> callback_list; | ||
| 204 | int last_callback_key = 0; | ||
| 205 | |||
| 206 | // Stores the current status of all external device input | ||
| 207 | DeviceStatus device_status; | ||
| 208 | }; | ||
| 209 | |||
| 210 | } // 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..a1c3bbb57 --- /dev/null +++ b/src/core/hid/hid_core.cpp | |||
| @@ -0,0 +1,214 @@ | |||
| 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 | player_1->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 112 | player_2->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 113 | player_3->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 114 | player_4->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 115 | player_5->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 116 | player_6->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 117 | player_7->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 118 | player_8->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 119 | other->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 120 | handheld->SetSupportedNpadStyleTag(supported_style_tag); | ||
| 121 | } | ||
| 122 | |||
| 123 | NpadStyleTag HIDCore::GetSupportedStyleTag() const { | ||
| 124 | return supported_style_tag; | ||
| 125 | } | ||
| 126 | |||
| 127 | s8 HIDCore::GetPlayerCount() const { | ||
| 128 | s8 active_players = 0; | ||
| 129 | for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) { | ||
| 130 | const auto* const controller = GetEmulatedControllerByIndex(player_index); | ||
| 131 | if (controller->IsConnected()) { | ||
| 132 | active_players++; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | return active_players; | ||
| 136 | } | ||
| 137 | |||
| 138 | NpadIdType HIDCore::GetFirstNpadId() const { | ||
| 139 | for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) { | ||
| 140 | const auto* const controller = GetEmulatedControllerByIndex(player_index); | ||
| 141 | if (controller->IsConnected()) { | ||
| 142 | return controller->GetNpadIdType(); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | return NpadIdType::Player1; | ||
| 146 | } | ||
| 147 | |||
| 148 | NpadIdType HIDCore::GetFirstDisconnectedNpadId() const { | ||
| 149 | for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) { | ||
| 150 | const auto* const controller = GetEmulatedControllerByIndex(player_index); | ||
| 151 | if (!controller->IsConnected()) { | ||
| 152 | return controller->GetNpadIdType(); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | return NpadIdType::Player1; | ||
| 156 | } | ||
| 157 | |||
| 158 | void HIDCore::EnableAllControllerConfiguration() { | ||
| 159 | player_1->EnableConfiguration(); | ||
| 160 | player_2->EnableConfiguration(); | ||
| 161 | player_3->EnableConfiguration(); | ||
| 162 | player_4->EnableConfiguration(); | ||
| 163 | player_5->EnableConfiguration(); | ||
| 164 | player_6->EnableConfiguration(); | ||
| 165 | player_7->EnableConfiguration(); | ||
| 166 | player_8->EnableConfiguration(); | ||
| 167 | other->EnableConfiguration(); | ||
| 168 | handheld->EnableConfiguration(); | ||
| 169 | } | ||
| 170 | |||
| 171 | void HIDCore::DisableAllControllerConfiguration() { | ||
| 172 | player_1->DisableConfiguration(); | ||
| 173 | player_2->DisableConfiguration(); | ||
| 174 | player_3->DisableConfiguration(); | ||
| 175 | player_4->DisableConfiguration(); | ||
| 176 | player_5->DisableConfiguration(); | ||
| 177 | player_6->DisableConfiguration(); | ||
| 178 | player_7->DisableConfiguration(); | ||
| 179 | player_8->DisableConfiguration(); | ||
| 180 | other->DisableConfiguration(); | ||
| 181 | handheld->DisableConfiguration(); | ||
| 182 | } | ||
| 183 | |||
| 184 | void HIDCore::ReloadInputDevices() { | ||
| 185 | player_1->ReloadFromSettings(); | ||
| 186 | player_2->ReloadFromSettings(); | ||
| 187 | player_3->ReloadFromSettings(); | ||
| 188 | player_4->ReloadFromSettings(); | ||
| 189 | player_5->ReloadFromSettings(); | ||
| 190 | player_6->ReloadFromSettings(); | ||
| 191 | player_7->ReloadFromSettings(); | ||
| 192 | player_8->ReloadFromSettings(); | ||
| 193 | other->ReloadFromSettings(); | ||
| 194 | handheld->ReloadFromSettings(); | ||
| 195 | console->ReloadFromSettings(); | ||
| 196 | devices->ReloadFromSettings(); | ||
| 197 | } | ||
| 198 | |||
| 199 | void HIDCore::UnloadInputDevices() { | ||
| 200 | player_1->UnloadInput(); | ||
| 201 | player_2->UnloadInput(); | ||
| 202 | player_3->UnloadInput(); | ||
| 203 | player_4->UnloadInput(); | ||
| 204 | player_5->UnloadInput(); | ||
| 205 | player_6->UnloadInput(); | ||
| 206 | player_7->UnloadInput(); | ||
| 207 | player_8->UnloadInput(); | ||
| 208 | other->UnloadInput(); | ||
| 209 | handheld->UnloadInput(); | ||
| 210 | console->UnloadInput(); | ||
| 211 | devices->UnloadInput(); | ||
| 212 | } | ||
| 213 | |||
| 214 | } // 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..837f7de49 --- /dev/null +++ b/src/core/hid/hid_core.h | |||
| @@ -0,0 +1,82 @@ | |||
| 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 | /// Returns the first disconnected npad id | ||
| 49 | NpadIdType GetFirstDisconnectedNpadId() const; | ||
| 50 | |||
| 51 | /// Sets all emulated controllers into configuring mode. | ||
| 52 | void EnableAllControllerConfiguration(); | ||
| 53 | |||
| 54 | /// Sets all emulated controllers into normal mode. | ||
| 55 | void DisableAllControllerConfiguration(); | ||
| 56 | |||
| 57 | /// Reloads all input devices from settings | ||
| 58 | void ReloadInputDevices(); | ||
| 59 | |||
| 60 | /// Removes all callbacks from input common | ||
| 61 | void UnloadInputDevices(); | ||
| 62 | |||
| 63 | /// Number of emulated controllers | ||
| 64 | static constexpr std::size_t available_controllers{10}; | ||
| 65 | |||
| 66 | private: | ||
| 67 | std::unique_ptr<EmulatedController> player_1; | ||
| 68 | std::unique_ptr<EmulatedController> player_2; | ||
| 69 | std::unique_ptr<EmulatedController> player_3; | ||
| 70 | std::unique_ptr<EmulatedController> player_4; | ||
| 71 | std::unique_ptr<EmulatedController> player_5; | ||
| 72 | std::unique_ptr<EmulatedController> player_6; | ||
| 73 | std::unique_ptr<EmulatedController> player_7; | ||
| 74 | std::unique_ptr<EmulatedController> player_8; | ||
| 75 | std::unique_ptr<EmulatedController> other; | ||
| 76 | std::unique_ptr<EmulatedController> handheld; | ||
| 77 | std::unique_ptr<EmulatedConsole> console; | ||
| 78 | std::unique_ptr<EmulatedDevices> devices; | ||
| 79 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; | ||
| 80 | }; | ||
| 81 | |||
| 82 | } // 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..7c12f01fc --- /dev/null +++ b/src/core/hid/hid_types.h | |||
| @@ -0,0 +1,635 @@ | |||
| 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 | All = 0xFFFFFFFFFFFFFFFFULL, | ||
| 68 | }; | ||
| 69 | DECLARE_ENUM_FLAG_OPERATORS(NpadButton); | ||
| 70 | |||
| 71 | enum class KeyboardKeyIndex : u32 { | ||
| 72 | A = 4, | ||
| 73 | B = 5, | ||
| 74 | C = 6, | ||
| 75 | D = 7, | ||
| 76 | E = 8, | ||
| 77 | F = 9, | ||
| 78 | G = 10, | ||
| 79 | H = 11, | ||
| 80 | I = 12, | ||
| 81 | J = 13, | ||
| 82 | K = 14, | ||
| 83 | L = 15, | ||
| 84 | M = 16, | ||
| 85 | N = 17, | ||
| 86 | O = 18, | ||
| 87 | P = 19, | ||
| 88 | Q = 20, | ||
| 89 | R = 21, | ||
| 90 | S = 22, | ||
| 91 | T = 23, | ||
| 92 | U = 24, | ||
| 93 | V = 25, | ||
| 94 | W = 26, | ||
| 95 | X = 27, | ||
| 96 | Y = 28, | ||
| 97 | Z = 29, | ||
| 98 | D1 = 30, | ||
| 99 | D2 = 31, | ||
| 100 | D3 = 32, | ||
| 101 | D4 = 33, | ||
| 102 | D5 = 34, | ||
| 103 | D6 = 35, | ||
| 104 | D7 = 36, | ||
| 105 | D8 = 37, | ||
| 106 | D9 = 38, | ||
| 107 | D0 = 39, | ||
| 108 | Return = 40, | ||
| 109 | Escape = 41, | ||
| 110 | Backspace = 42, | ||
| 111 | Tab = 43, | ||
| 112 | Space = 44, | ||
| 113 | Minus = 45, | ||
| 114 | Plus = 46, | ||
| 115 | OpenBracket = 47, | ||
| 116 | CloseBracket = 48, | ||
| 117 | Pipe = 49, | ||
| 118 | Tilde = 50, | ||
| 119 | Semicolon = 51, | ||
| 120 | Quote = 52, | ||
| 121 | Backquote = 53, | ||
| 122 | Comma = 54, | ||
| 123 | Period = 55, | ||
| 124 | Slash = 56, | ||
| 125 | CapsLock = 57, | ||
| 126 | F1 = 58, | ||
| 127 | F2 = 59, | ||
| 128 | F3 = 60, | ||
| 129 | F4 = 61, | ||
| 130 | F5 = 62, | ||
| 131 | F6 = 63, | ||
| 132 | F7 = 64, | ||
| 133 | F8 = 65, | ||
| 134 | F9 = 66, | ||
| 135 | F10 = 67, | ||
| 136 | F11 = 68, | ||
| 137 | F12 = 69, | ||
| 138 | PrintScreen = 70, | ||
| 139 | ScrollLock = 71, | ||
| 140 | Pause = 72, | ||
| 141 | Insert = 73, | ||
| 142 | Home = 74, | ||
| 143 | PageUp = 75, | ||
| 144 | Delete = 76, | ||
| 145 | End = 77, | ||
| 146 | PageDown = 78, | ||
| 147 | RightArrow = 79, | ||
| 148 | LeftArrow = 80, | ||
| 149 | DownArrow = 81, | ||
| 150 | UpArrow = 82, | ||
| 151 | NumLock = 83, | ||
| 152 | NumPadDivide = 84, | ||
| 153 | NumPadMultiply = 85, | ||
| 154 | NumPadSubtract = 86, | ||
| 155 | NumPadAdd = 87, | ||
| 156 | NumPadEnter = 88, | ||
| 157 | NumPad1 = 89, | ||
| 158 | NumPad2 = 90, | ||
| 159 | NumPad3 = 91, | ||
| 160 | NumPad4 = 92, | ||
| 161 | NumPad5 = 93, | ||
| 162 | NumPad6 = 94, | ||
| 163 | NumPad7 = 95, | ||
| 164 | NumPad8 = 96, | ||
| 165 | NumPad9 = 97, | ||
| 166 | NumPad0 = 98, | ||
| 167 | NumPadDot = 99, | ||
| 168 | Backslash = 100, | ||
| 169 | Application = 101, | ||
| 170 | Power = 102, | ||
| 171 | NumPadEquals = 103, | ||
| 172 | F13 = 104, | ||
| 173 | F14 = 105, | ||
| 174 | F15 = 106, | ||
| 175 | F16 = 107, | ||
| 176 | F17 = 108, | ||
| 177 | F18 = 109, | ||
| 178 | F19 = 110, | ||
| 179 | F20 = 111, | ||
| 180 | F21 = 112, | ||
| 181 | F22 = 113, | ||
| 182 | F23 = 114, | ||
| 183 | F24 = 115, | ||
| 184 | NumPadComma = 133, | ||
| 185 | Ro = 135, | ||
| 186 | KatakanaHiragana = 136, | ||
| 187 | Yen = 137, | ||
| 188 | Henkan = 138, | ||
| 189 | Muhenkan = 139, | ||
| 190 | NumPadCommaPc98 = 140, | ||
| 191 | HangulEnglish = 144, | ||
| 192 | Hanja = 145, | ||
| 193 | Katakana = 146, | ||
| 194 | Hiragana = 147, | ||
| 195 | ZenkakuHankaku = 148, | ||
| 196 | LeftControl = 224, | ||
| 197 | LeftShift = 225, | ||
| 198 | LeftAlt = 226, | ||
| 199 | LeftGui = 227, | ||
| 200 | RightControl = 228, | ||
| 201 | RightShift = 229, | ||
| 202 | RightAlt = 230, | ||
| 203 | RightGui = 231, | ||
| 204 | }; | ||
| 205 | |||
| 206 | // This is nn::hid::NpadIdType | ||
| 207 | enum class NpadIdType : u32 { | ||
| 208 | Player1 = 0x0, | ||
| 209 | Player2 = 0x1, | ||
| 210 | Player3 = 0x2, | ||
| 211 | Player4 = 0x3, | ||
| 212 | Player5 = 0x4, | ||
| 213 | Player6 = 0x5, | ||
| 214 | Player7 = 0x6, | ||
| 215 | Player8 = 0x7, | ||
| 216 | Other = 0x10, | ||
| 217 | Handheld = 0x20, | ||
| 218 | |||
| 219 | Invalid = 0xFFFFFFFF, | ||
| 220 | }; | ||
| 221 | |||
| 222 | // This is nn::hid::NpadStyleIndex | ||
| 223 | enum class NpadStyleIndex : u8 { | ||
| 224 | None = 0, | ||
| 225 | ProController = 3, | ||
| 226 | Handheld = 4, | ||
| 227 | HandheldNES = 4, | ||
| 228 | JoyconDual = 5, | ||
| 229 | JoyconLeft = 6, | ||
| 230 | JoyconRight = 7, | ||
| 231 | GameCube = 8, | ||
| 232 | Pokeball = 9, | ||
| 233 | NES = 10, | ||
| 234 | SNES = 12, | ||
| 235 | N64 = 13, | ||
| 236 | SegaGenesis = 14, | ||
| 237 | SystemExt = 32, | ||
| 238 | System = 33, | ||
| 239 | MaxNpadType = 34, | ||
| 240 | }; | ||
| 241 | |||
| 242 | // This is nn::hid::NpadStyleSet | ||
| 243 | enum class NpadStyleSet : u32 { | ||
| 244 | None = 0, | ||
| 245 | Fullkey = 1U << 0, | ||
| 246 | Handheld = 1U << 1, | ||
| 247 | JoyDual = 1U << 2, | ||
| 248 | JoyLeft = 1U << 3, | ||
| 249 | JoyRight = 1U << 4, | ||
| 250 | Gc = 1U << 5, | ||
| 251 | Palma = 1U << 6, | ||
| 252 | Lark = 1U << 7, | ||
| 253 | HandheldLark = 1U << 8, | ||
| 254 | Lucia = 1U << 9, | ||
| 255 | Lagoon = 1U << 10, | ||
| 256 | Lager = 1U << 11, | ||
| 257 | SystemExt = 1U << 29, | ||
| 258 | System = 1U << 30, | ||
| 259 | |||
| 260 | All = 0xFFFFFFFFU, | ||
| 261 | }; | ||
| 262 | static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); | ||
| 263 | |||
| 264 | // This is nn::hid::VibrationDevicePosition | ||
| 265 | enum class VibrationDevicePosition : u32 { | ||
| 266 | None = 0, | ||
| 267 | Left = 1, | ||
| 268 | Right = 2, | ||
| 269 | }; | ||
| 270 | |||
| 271 | // This is nn::hid::VibrationDeviceType | ||
| 272 | enum class VibrationDeviceType : u32 { | ||
| 273 | Unknown = 0, | ||
| 274 | LinearResonantActuator = 1, | ||
| 275 | GcErm = 2, | ||
| 276 | }; | ||
| 277 | |||
| 278 | // This is nn::hid::VibrationGcErmCommand | ||
| 279 | enum class VibrationGcErmCommand : u64 { | ||
| 280 | Stop = 0, | ||
| 281 | Start = 1, | ||
| 282 | StopHard = 2, | ||
| 283 | }; | ||
| 284 | |||
| 285 | // This is nn::hid::NpadStyleTag | ||
| 286 | struct NpadStyleTag { | ||
| 287 | union { | ||
| 288 | NpadStyleSet raw{}; | ||
| 289 | |||
| 290 | BitField<0, 1, u32> fullkey; | ||
| 291 | BitField<1, 1, u32> handheld; | ||
| 292 | BitField<2, 1, u32> joycon_dual; | ||
| 293 | BitField<3, 1, u32> joycon_left; | ||
| 294 | BitField<4, 1, u32> joycon_right; | ||
| 295 | BitField<5, 1, u32> gamecube; | ||
| 296 | BitField<6, 1, u32> palma; | ||
| 297 | BitField<7, 1, u32> lark; | ||
| 298 | BitField<8, 1, u32> handheld_lark; | ||
| 299 | BitField<9, 1, u32> lucia; | ||
| 300 | BitField<10, 1, u32> lagoon; | ||
| 301 | BitField<11, 1, u32> lager; | ||
| 302 | BitField<29, 1, u32> system_ext; | ||
| 303 | BitField<30, 1, u32> system; | ||
| 304 | }; | ||
| 305 | }; | ||
| 306 | static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size"); | ||
| 307 | |||
| 308 | // This is nn::hid::TouchAttribute | ||
| 309 | struct TouchAttribute { | ||
| 310 | union { | ||
| 311 | u32 raw{}; | ||
| 312 | BitField<0, 1, u32> start_touch; | ||
| 313 | BitField<1, 1, u32> end_touch; | ||
| 314 | }; | ||
| 315 | }; | ||
| 316 | static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); | ||
| 317 | |||
| 318 | // This is nn::hid::TouchState | ||
| 319 | struct TouchState { | ||
| 320 | u64 delta_time; | ||
| 321 | TouchAttribute attribute; | ||
| 322 | u32 finger; | ||
| 323 | Common::Point<u32> position; | ||
| 324 | u32 diameter_x; | ||
| 325 | u32 diameter_y; | ||
| 326 | u32 rotation_angle; | ||
| 327 | }; | ||
| 328 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | ||
| 329 | |||
| 330 | // This is nn::hid::NpadControllerColor | ||
| 331 | struct NpadControllerColor { | ||
| 332 | u32 body; | ||
| 333 | u32 button; | ||
| 334 | }; | ||
| 335 | static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); | ||
| 336 | |||
| 337 | // This is nn::hid::AnalogStickState | ||
| 338 | struct AnalogStickState { | ||
| 339 | s32 x; | ||
| 340 | s32 y; | ||
| 341 | }; | ||
| 342 | static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size"); | ||
| 343 | |||
| 344 | // This is nn::hid::server::NpadGcTriggerState | ||
| 345 | struct NpadGcTriggerState { | ||
| 346 | s64 sampling_number{}; | ||
| 347 | s32 left{}; | ||
| 348 | s32 right{}; | ||
| 349 | }; | ||
| 350 | static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); | ||
| 351 | |||
| 352 | // This is nn::hid::system::NpadBatteryLevel | ||
| 353 | using NpadBatteryLevel = u32; | ||
| 354 | static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size"); | ||
| 355 | |||
| 356 | // This is nn::hid::system::NpadPowerInfo | ||
| 357 | struct NpadPowerInfo { | ||
| 358 | bool is_powered; | ||
| 359 | bool is_charging; | ||
| 360 | INSERT_PADDING_BYTES(0x6); | ||
| 361 | NpadBatteryLevel battery_level; | ||
| 362 | }; | ||
| 363 | static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); | ||
| 364 | |||
| 365 | struct LedPattern { | ||
| 366 | explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { | ||
| 367 | position1.Assign(light1); | ||
| 368 | position2.Assign(light2); | ||
| 369 | position3.Assign(light3); | ||
| 370 | position4.Assign(light4); | ||
| 371 | } | ||
| 372 | union { | ||
| 373 | u64 raw{}; | ||
| 374 | BitField<0, 1, u64> position1; | ||
| 375 | BitField<1, 1, u64> position2; | ||
| 376 | BitField<2, 1, u64> position3; | ||
| 377 | BitField<3, 1, u64> position4; | ||
| 378 | }; | ||
| 379 | }; | ||
| 380 | |||
| 381 | struct NpadButtonState { | ||
| 382 | union { | ||
| 383 | NpadButton raw{}; | ||
| 384 | |||
| 385 | // Buttons | ||
| 386 | BitField<0, 1, u64> a; | ||
| 387 | BitField<1, 1, u64> b; | ||
| 388 | BitField<2, 1, u64> x; | ||
| 389 | BitField<3, 1, u64> y; | ||
| 390 | BitField<4, 1, u64> stick_l; | ||
| 391 | BitField<5, 1, u64> stick_r; | ||
| 392 | BitField<6, 1, u64> l; | ||
| 393 | BitField<7, 1, u64> r; | ||
| 394 | BitField<8, 1, u64> zl; | ||
| 395 | BitField<9, 1, u64> zr; | ||
| 396 | BitField<10, 1, u64> plus; | ||
| 397 | BitField<11, 1, u64> minus; | ||
| 398 | |||
| 399 | // D-Pad | ||
| 400 | BitField<12, 1, u64> left; | ||
| 401 | BitField<13, 1, u64> up; | ||
| 402 | BitField<14, 1, u64> right; | ||
| 403 | BitField<15, 1, u64> down; | ||
| 404 | |||
| 405 | // Left JoyStick | ||
| 406 | BitField<16, 1, u64> stick_l_left; | ||
| 407 | BitField<17, 1, u64> stick_l_up; | ||
| 408 | BitField<18, 1, u64> stick_l_right; | ||
| 409 | BitField<19, 1, u64> stick_l_down; | ||
| 410 | |||
| 411 | // Right JoyStick | ||
| 412 | BitField<20, 1, u64> stick_r_left; | ||
| 413 | BitField<21, 1, u64> stick_r_up; | ||
| 414 | BitField<22, 1, u64> stick_r_right; | ||
| 415 | BitField<23, 1, u64> stick_r_down; | ||
| 416 | |||
| 417 | BitField<24, 1, u64> left_sl; | ||
| 418 | BitField<25, 1, u64> left_sr; | ||
| 419 | |||
| 420 | BitField<26, 1, u64> right_sl; | ||
| 421 | BitField<27, 1, u64> right_sr; | ||
| 422 | |||
| 423 | BitField<28, 1, u64> palma; | ||
| 424 | BitField<29, 1, u64> verification; | ||
| 425 | BitField<30, 1, u64> handheld_left_b; | ||
| 426 | BitField<31, 1, u64> lagon_c_left; | ||
| 427 | BitField<32, 1, u64> lagon_c_up; | ||
| 428 | BitField<33, 1, u64> lagon_c_right; | ||
| 429 | BitField<34, 1, u64> lagon_c_down; | ||
| 430 | }; | ||
| 431 | }; | ||
| 432 | static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size."); | ||
| 433 | |||
| 434 | // This is nn::hid::DebugPadButton | ||
| 435 | struct DebugPadButton { | ||
| 436 | union { | ||
| 437 | u32 raw{}; | ||
| 438 | BitField<0, 1, u32> a; | ||
| 439 | BitField<1, 1, u32> b; | ||
| 440 | BitField<2, 1, u32> x; | ||
| 441 | BitField<3, 1, u32> y; | ||
| 442 | BitField<4, 1, u32> l; | ||
| 443 | BitField<5, 1, u32> r; | ||
| 444 | BitField<6, 1, u32> zl; | ||
| 445 | BitField<7, 1, u32> zr; | ||
| 446 | BitField<8, 1, u32> plus; | ||
| 447 | BitField<9, 1, u32> minus; | ||
| 448 | BitField<10, 1, u32> d_left; | ||
| 449 | BitField<11, 1, u32> d_up; | ||
| 450 | BitField<12, 1, u32> d_right; | ||
| 451 | BitField<13, 1, u32> d_down; | ||
| 452 | }; | ||
| 453 | }; | ||
| 454 | static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size"); | ||
| 455 | |||
| 456 | // This is nn::hid::ConsoleSixAxisSensorHandle | ||
| 457 | struct ConsoleSixAxisSensorHandle { | ||
| 458 | u8 unknown_1; | ||
| 459 | u8 unknown_2; | ||
| 460 | INSERT_PADDING_BYTES_NOINIT(2); | ||
| 461 | }; | ||
| 462 | static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4, | ||
| 463 | "ConsoleSixAxisSensorHandle is an invalid size"); | ||
| 464 | |||
| 465 | // This is nn::hid::SixAxisSensorHandle | ||
| 466 | struct SixAxisSensorHandle { | ||
| 467 | NpadStyleIndex npad_type; | ||
| 468 | u8 npad_id; | ||
| 469 | DeviceIndex device_index; | ||
| 470 | INSERT_PADDING_BYTES_NOINIT(1); | ||
| 471 | }; | ||
| 472 | static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); | ||
| 473 | |||
| 474 | struct SixAxisSensorFusionParameters { | ||
| 475 | f32 parameter1; | ||
| 476 | f32 parameter2; | ||
| 477 | }; | ||
| 478 | static_assert(sizeof(SixAxisSensorFusionParameters) == 8, | ||
| 479 | "SixAxisSensorFusionParameters is an invalid size"); | ||
| 480 | |||
| 481 | // This is nn::hid::VibrationDeviceHandle | ||
| 482 | struct VibrationDeviceHandle { | ||
| 483 | NpadStyleIndex npad_type; | ||
| 484 | u8 npad_id; | ||
| 485 | DeviceIndex device_index; | ||
| 486 | INSERT_PADDING_BYTES_NOINIT(1); | ||
| 487 | }; | ||
| 488 | static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size"); | ||
| 489 | |||
| 490 | // This is nn::hid::VibrationValue | ||
| 491 | struct VibrationValue { | ||
| 492 | f32 low_amplitude; | ||
| 493 | f32 low_frequency; | ||
| 494 | f32 high_amplitude; | ||
| 495 | f32 high_frequency; | ||
| 496 | }; | ||
| 497 | static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); | ||
| 498 | |||
| 499 | // This is nn::hid::VibrationDeviceInfo | ||
| 500 | struct VibrationDeviceInfo { | ||
| 501 | VibrationDeviceType type{}; | ||
| 502 | VibrationDevicePosition position{}; | ||
| 503 | }; | ||
| 504 | static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); | ||
| 505 | |||
| 506 | // This is nn::hid::KeyboardModifier | ||
| 507 | struct KeyboardModifier { | ||
| 508 | union { | ||
| 509 | u32 raw{}; | ||
| 510 | BitField<0, 1, u32> control; | ||
| 511 | BitField<1, 1, u32> shift; | ||
| 512 | BitField<2, 1, u32> left_alt; | ||
| 513 | BitField<3, 1, u32> right_alt; | ||
| 514 | BitField<4, 1, u32> gui; | ||
| 515 | BitField<8, 1, u32> caps_lock; | ||
| 516 | BitField<9, 1, u32> scroll_lock; | ||
| 517 | BitField<10, 1, u32> num_lock; | ||
| 518 | BitField<11, 1, u32> katakana; | ||
| 519 | BitField<12, 1, u32> hiragana; | ||
| 520 | }; | ||
| 521 | }; | ||
| 522 | |||
| 523 | static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size"); | ||
| 524 | |||
| 525 | // This is nn::hid::KeyboardAttribute | ||
| 526 | struct KeyboardAttribute { | ||
| 527 | union { | ||
| 528 | u32 raw{}; | ||
| 529 | BitField<0, 1, u32> is_connected; | ||
| 530 | }; | ||
| 531 | }; | ||
| 532 | static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size"); | ||
| 533 | |||
| 534 | // This is nn::hid::KeyboardKey | ||
| 535 | struct KeyboardKey { | ||
| 536 | // This should be a 256 bit flag | ||
| 537 | std::array<u8, 32> key; | ||
| 538 | }; | ||
| 539 | static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size"); | ||
| 540 | |||
| 541 | // This is nn::hid::MouseButton | ||
| 542 | struct MouseButton { | ||
| 543 | union { | ||
| 544 | u32_le raw{}; | ||
| 545 | BitField<0, 1, u32> left; | ||
| 546 | BitField<1, 1, u32> right; | ||
| 547 | BitField<2, 1, u32> middle; | ||
| 548 | BitField<3, 1, u32> forward; | ||
| 549 | BitField<4, 1, u32> back; | ||
| 550 | }; | ||
| 551 | }; | ||
| 552 | static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size"); | ||
| 553 | |||
| 554 | // This is nn::hid::MouseAttribute | ||
| 555 | struct MouseAttribute { | ||
| 556 | union { | ||
| 557 | u32 raw{}; | ||
| 558 | BitField<0, 1, u32> transferable; | ||
| 559 | BitField<1, 1, u32> is_connected; | ||
| 560 | }; | ||
| 561 | }; | ||
| 562 | static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size"); | ||
| 563 | |||
| 564 | // This is nn::hid::detail::MouseState | ||
| 565 | struct MouseState { | ||
| 566 | s64 sampling_number; | ||
| 567 | s32 x; | ||
| 568 | s32 y; | ||
| 569 | s32 delta_x; | ||
| 570 | s32 delta_y; | ||
| 571 | // Axis Order in HW is switched for the wheel | ||
| 572 | s32 delta_wheel_y; | ||
| 573 | s32 delta_wheel_x; | ||
| 574 | MouseButton button; | ||
| 575 | MouseAttribute attribute; | ||
| 576 | }; | ||
| 577 | static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); | ||
| 578 | |||
| 579 | /// Converts a NpadIdType to an array index. | ||
| 580 | constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) { | ||
| 581 | switch (npad_id_type) { | ||
| 582 | case NpadIdType::Player1: | ||
| 583 | return 0; | ||
| 584 | case NpadIdType::Player2: | ||
| 585 | return 1; | ||
| 586 | case NpadIdType::Player3: | ||
| 587 | return 2; | ||
| 588 | case NpadIdType::Player4: | ||
| 589 | return 3; | ||
| 590 | case NpadIdType::Player5: | ||
| 591 | return 4; | ||
| 592 | case NpadIdType::Player6: | ||
| 593 | return 5; | ||
| 594 | case NpadIdType::Player7: | ||
| 595 | return 6; | ||
| 596 | case NpadIdType::Player8: | ||
| 597 | return 7; | ||
| 598 | case NpadIdType::Handheld: | ||
| 599 | return 8; | ||
| 600 | case NpadIdType::Other: | ||
| 601 | return 9; | ||
| 602 | default: | ||
| 603 | return 0; | ||
| 604 | } | ||
| 605 | } | ||
| 606 | |||
| 607 | /// Converts an array index to a NpadIdType | ||
| 608 | constexpr NpadIdType IndexToNpadIdType(size_t index) { | ||
| 609 | switch (index) { | ||
| 610 | case 0: | ||
| 611 | return NpadIdType::Player1; | ||
| 612 | case 1: | ||
| 613 | return NpadIdType::Player2; | ||
| 614 | case 2: | ||
| 615 | return NpadIdType::Player3; | ||
| 616 | case 3: | ||
| 617 | return NpadIdType::Player4; | ||
| 618 | case 4: | ||
| 619 | return NpadIdType::Player5; | ||
| 620 | case 5: | ||
| 621 | return NpadIdType::Player6; | ||
| 622 | case 6: | ||
| 623 | return NpadIdType::Player7; | ||
| 624 | case 7: | ||
| 625 | return NpadIdType::Player8; | ||
| 626 | case 8: | ||
| 627 | return NpadIdType::Handheld; | ||
| 628 | case 9: | ||
| 629 | return NpadIdType::Other; | ||
| 630 | default: | ||
| 631 | return NpadIdType::Invalid; | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | } // 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..d24582226 --- /dev/null +++ b/src/core/hid/input_converter.h | |||
| @@ -0,0 +1,96 @@ | |||
| 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 callback 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 callback 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 callback 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 callback 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 callback 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 callback 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 callback 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 analog An analog object containing raw data and properties | ||
| 83 | * @param clamp_value determines if the value 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 analog_x raw analog data and properties for the x-axis | ||
| 90 | * @param analog_y raw analog data and properties for the y-axis | ||
| 91 | * @param clamp_value bool that determines if the value needs to be clamped into the unit circle. | ||
| 92 | */ | ||
| 93 | void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y, | ||
| 94 | bool clamp_value); | ||
| 95 | |||
| 96 | } // namespace Core::HID | ||
diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp index 9f6a90e8f..2dbda8814 100644 --- a/src/core/frontend/input_interpreter.cpp +++ b/src/core/hid/input_interpreter.cpp | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/frontend/input_interpreter.h" | 6 | #include "core/hid/hid_types.h" |
| 7 | #include "core/hid/input_interpreter.h" | ||
| 7 | #include "core/hle/service/hid/controllers/npad.h" | 8 | #include "core/hle/service/hid/controllers/npad.h" |
| 8 | #include "core/hle/service/hid/hid.h" | 9 | #include "core/hle/service/hid/hid.h" |
| 9 | #include "core/hle/service/sm/sm.h" | 10 | #include "core/hle/service/sm/sm.h" |
| @@ -19,7 +20,7 @@ InputInterpreter::InputInterpreter(Core::System& system) | |||
| 19 | InputInterpreter::~InputInterpreter() = default; | 20 | InputInterpreter::~InputInterpreter() = default; |
| 20 | 21 | ||
| 21 | void InputInterpreter::PollInput() { | 22 | void InputInterpreter::PollInput() { |
| 22 | const u32 button_state = npad.GetAndResetPressState(); | 23 | const auto button_state = npad.GetAndResetPressState(); |
| 23 | 24 | ||
| 24 | previous_index = current_index; | 25 | previous_index = current_index; |
| 25 | current_index = (current_index + 1) % button_states.size(); | 26 | current_index = (current_index + 1) % button_states.size(); |
| @@ -31,32 +32,30 @@ void InputInterpreter::ResetButtonStates() { | |||
| 31 | previous_index = 0; | 32 | previous_index = 0; |
| 32 | current_index = 0; | 33 | current_index = 0; |
| 33 | 34 | ||
| 34 | button_states[0] = 0xFFFFFFFF; | 35 | button_states[0] = Core::HID::NpadButton::All; |
| 35 | 36 | ||
| 36 | for (std::size_t i = 1; i < button_states.size(); ++i) { | 37 | for (std::size_t i = 1; i < button_states.size(); ++i) { |
| 37 | button_states[i] = 0; | 38 | button_states[i] = Core::HID::NpadButton::None; |
| 38 | } | 39 | } |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | bool InputInterpreter::IsButtonPressed(HIDButton button) const { | 42 | bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const { |
| 42 | return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; | 43 | return True(button_states[current_index] & button); |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 45 | bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const { | 46 | bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const { |
| 46 | const bool current_press = | 47 | const bool current_press = True(button_states[current_index] & button); |
| 47 | (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; | 48 | const bool previous_press = True(button_states[previous_index] & button); |
| 48 | const bool previous_press = | ||
| 49 | (button_states[previous_index] & (1U << static_cast<u8>(button))) != 0; | ||
| 50 | 49 | ||
| 51 | return current_press && !previous_press; | 50 | return current_press && !previous_press; |
| 52 | } | 51 | } |
| 53 | 52 | ||
| 54 | bool InputInterpreter::IsButtonHeld(HIDButton button) const { | 53 | bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const { |
| 55 | u32 held_buttons{button_states[0]}; | 54 | Core::HID::NpadButton held_buttons{button_states[0]}; |
| 56 | 55 | ||
| 57 | for (std::size_t i = 1; i < button_states.size(); ++i) { | 56 | for (std::size_t i = 1; i < button_states.size(); ++i) { |
| 58 | held_buttons &= button_states[i]; | 57 | held_buttons &= button_states[i]; |
| 59 | } | 58 | } |
| 60 | 59 | ||
| 61 | return (held_buttons & (1U << static_cast<u8>(button))) != 0; | 60 | return True(held_buttons & button); |
| 62 | } | 61 | } |
diff --git a/src/core/frontend/input_interpreter.h b/src/core/hid/input_interpreter.h index 9495e3daf..70c34d474 100644 --- a/src/core/frontend/input_interpreter.h +++ b/src/core/hid/input_interpreter.h | |||
| @@ -12,46 +12,14 @@ namespace Core { | |||
| 12 | class System; | 12 | class System; |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | namespace Core::HID { | ||
| 16 | enum class NpadButton : u64; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Service::HID { | 19 | namespace Service::HID { |
| 16 | class Controller_NPad; | 20 | class Controller_NPad; |
| 17 | } | 21 | } |
| 18 | 22 | ||
| 19 | enum class HIDButton : u8 { | ||
| 20 | A, | ||
| 21 | B, | ||
| 22 | X, | ||
| 23 | Y, | ||
| 24 | LStick, | ||
| 25 | RStick, | ||
| 26 | L, | ||
| 27 | R, | ||
| 28 | ZL, | ||
| 29 | ZR, | ||
| 30 | Plus, | ||
| 31 | Minus, | ||
| 32 | |||
| 33 | DLeft, | ||
| 34 | DUp, | ||
| 35 | DRight, | ||
| 36 | DDown, | ||
| 37 | |||
| 38 | LStickLeft, | ||
| 39 | LStickUp, | ||
| 40 | LStickRight, | ||
| 41 | LStickDown, | ||
| 42 | |||
| 43 | RStickLeft, | ||
| 44 | RStickUp, | ||
| 45 | RStickRight, | ||
| 46 | RStickDown, | ||
| 47 | |||
| 48 | LeftSL, | ||
| 49 | LeftSR, | ||
| 50 | |||
| 51 | RightSL, | ||
| 52 | RightSR, | ||
| 53 | }; | ||
| 54 | |||
| 55 | /** | 23 | /** |
| 56 | * The InputInterpreter class interfaces with HID to retrieve button press states. | 24 | * The InputInterpreter class interfaces with HID to retrieve button press states. |
| 57 | * Input is intended to be polled every 50ms so that a button is considered to be | 25 | * Input is intended to be polled every 50ms so that a button is considered to be |
| @@ -76,7 +44,7 @@ public: | |||
| 76 | * | 44 | * |
| 77 | * @returns True when the button is pressed. | 45 | * @returns True when the button is pressed. |
| 78 | */ | 46 | */ |
| 79 | [[nodiscard]] bool IsButtonPressed(HIDButton button) const; | 47 | [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const; |
| 80 | 48 | ||
| 81 | /** | 49 | /** |
| 82 | * Checks whether any of the buttons in the parameter list is pressed. | 50 | * Checks whether any of the buttons in the parameter list is pressed. |
| @@ -85,7 +53,7 @@ public: | |||
| 85 | * | 53 | * |
| 86 | * @returns True when at least one of the buttons is pressed. | 54 | * @returns True when at least one of the buttons is pressed. |
| 87 | */ | 55 | */ |
| 88 | template <HIDButton... T> | 56 | template <Core::HID::NpadButton... T> |
| 89 | [[nodiscard]] bool IsAnyButtonPressed() { | 57 | [[nodiscard]] bool IsAnyButtonPressed() { |
| 90 | return (IsButtonPressed(T) || ...); | 58 | return (IsButtonPressed(T) || ...); |
| 91 | } | 59 | } |
| @@ -98,7 +66,7 @@ public: | |||
| 98 | * | 66 | * |
| 99 | * @returns True when the button is pressed once. | 67 | * @returns True when the button is pressed once. |
| 100 | */ | 68 | */ |
| 101 | [[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const; | 69 | [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const; |
| 102 | 70 | ||
| 103 | /** | 71 | /** |
| 104 | * Checks whether any of the buttons in the parameter list is pressed once. | 72 | * Checks whether any of the buttons in the parameter list is pressed once. |
| @@ -107,7 +75,7 @@ public: | |||
| 107 | * | 75 | * |
| 108 | * @returns True when at least one of the buttons is pressed once. | 76 | * @returns True when at least one of the buttons is pressed once. |
| 109 | */ | 77 | */ |
| 110 | template <HIDButton... T> | 78 | template <Core::HID::NpadButton... T> |
| 111 | [[nodiscard]] bool IsAnyButtonPressedOnce() const { | 79 | [[nodiscard]] bool IsAnyButtonPressedOnce() const { |
| 112 | return (IsButtonPressedOnce(T) || ...); | 80 | return (IsButtonPressedOnce(T) || ...); |
| 113 | } | 81 | } |
| @@ -119,7 +87,7 @@ public: | |||
| 119 | * | 87 | * |
| 120 | * @returns True when the button is held down. | 88 | * @returns True when the button is held down. |
| 121 | */ | 89 | */ |
| 122 | [[nodiscard]] bool IsButtonHeld(HIDButton button) const; | 90 | [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const; |
| 123 | 91 | ||
| 124 | /** | 92 | /** |
| 125 | * Checks whether any of the buttons in the parameter list is held down. | 93 | * Checks whether any of the buttons in the parameter list is held down. |
| @@ -128,7 +96,7 @@ public: | |||
| 128 | * | 96 | * |
| 129 | * @returns True when at least one of the buttons is held down. | 97 | * @returns True when at least one of the buttons is held down. |
| 130 | */ | 98 | */ |
| 131 | template <HIDButton... T> | 99 | template <Core::HID::NpadButton... T> |
| 132 | [[nodiscard]] bool IsAnyButtonHeld() const { | 100 | [[nodiscard]] bool IsAnyButtonHeld() const { |
| 133 | return (IsButtonHeld(T) || ...); | 101 | return (IsButtonHeld(T) || ...); |
| 134 | } | 102 | } |
| @@ -137,7 +105,7 @@ private: | |||
| 137 | Service::HID::Controller_NPad& npad; | 105 | Service::HID::Controller_NPad& npad; |
| 138 | 106 | ||
| 139 | /// Stores 9 consecutive button states polled from HID. | 107 | /// Stores 9 consecutive button states polled from HID. |
| 140 | std::array<u32, 9> button_states{}; | 108 | std::array<Core::HID::NpadButton, 9> button_states{}; |
| 141 | 109 | ||
| 142 | std::size_t previous_index{}; | 110 | std::size_t previous_index{}; |
| 143 | std::size_t current_index{}; | 111 | std::size_t current_index{}; |
diff --git a/src/input_common/motion_input.cpp b/src/core/hid/motion_input.cpp index 1c9d561c0..c25fea966 100644 --- a/src/input_common/motion_input.cpp +++ b/src/core/hid/motion_input.cpp | |||
| @@ -2,13 +2,21 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included | 3 | // Refer to the license.txt file included |
| 4 | 4 | ||
| 5 | #include <random> | ||
| 6 | #include "common/math_util.h" | 5 | #include "common/math_util.h" |
| 7 | #include "input_common/motion_input.h" | 6 | #include "core/hid/motion_input.h" |
| 8 | 7 | ||
| 9 | namespace InputCommon { | 8 | namespace Core::HID { |
| 10 | 9 | ||
| 11 | MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) : kp(new_kp), ki(new_ki), kd(new_kd) {} | 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 | } | ||
| 12 | 20 | ||
| 13 | void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { | 21 | void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { |
| 14 | accel = acceleration; | 22 | accel = acceleration; |
| @@ -65,6 +73,8 @@ void MotionInput::UpdateRotation(u64 elapsed_time) { | |||
| 65 | rotations += gyro * sample_period; | 73 | rotations += gyro * sample_period; |
| 66 | } | 74 | } |
| 67 | 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 | ||
| 68 | void MotionInput::UpdateOrientation(u64 elapsed_time) { | 78 | void MotionInput::UpdateOrientation(u64 elapsed_time) { |
| 69 | if (!IsCalibrated(0.1f)) { | 79 | if (!IsCalibrated(0.1f)) { |
| 70 | ResetOrientation(); | 80 | ResetOrientation(); |
| @@ -190,43 +200,6 @@ Common::Vec3f MotionInput::GetRotations() const { | |||
| 190 | return rotations; | 200 | return rotations; |
| 191 | } | 201 | } |
| 192 | 202 | ||
| 193 | Input::MotionStatus MotionInput::GetMotion() const { | ||
| 194 | const Common::Vec3f gyroscope = GetGyroscope(); | ||
| 195 | const Common::Vec3f accelerometer = GetAcceleration(); | ||
| 196 | const Common::Vec3f rotation = GetRotations(); | ||
| 197 | const std::array<Common::Vec3f, 3> orientation = GetOrientation(); | ||
| 198 | const Common::Quaternion<f32> quaternion = GetQuaternion(); | ||
| 199 | return {accelerometer, gyroscope, rotation, orientation, quaternion}; | ||
| 200 | } | ||
| 201 | |||
| 202 | Input::MotionStatus MotionInput::GetRandomMotion(int accel_magnitude, int gyro_magnitude) const { | ||
| 203 | std::random_device device; | ||
| 204 | std::mt19937 gen(device()); | ||
| 205 | std::uniform_int_distribution<s16> distribution(-1000, 1000); | ||
| 206 | const Common::Vec3f gyroscope{ | ||
| 207 | static_cast<f32>(distribution(gen)) * 0.001f, | ||
| 208 | static_cast<f32>(distribution(gen)) * 0.001f, | ||
| 209 | static_cast<f32>(distribution(gen)) * 0.001f, | ||
| 210 | }; | ||
| 211 | const Common::Vec3f accelerometer{ | ||
| 212 | static_cast<f32>(distribution(gen)) * 0.001f, | ||
| 213 | static_cast<f32>(distribution(gen)) * 0.001f, | ||
| 214 | static_cast<f32>(distribution(gen)) * 0.001f, | ||
| 215 | }; | ||
| 216 | constexpr Common::Vec3f rotation; | ||
| 217 | constexpr std::array orientation{ | ||
| 218 | Common::Vec3f{1.0f, 0.0f, 0.0f}, | ||
| 219 | Common::Vec3f{0.0f, 1.0f, 0.0f}, | ||
| 220 | Common::Vec3f{0.0f, 0.0f, 1.0f}, | ||
| 221 | }; | ||
| 222 | constexpr Common::Quaternion<f32> quaternion{ | ||
| 223 | {0.0f, 0.0f, 0.0f}, | ||
| 224 | 1.0f, | ||
| 225 | }; | ||
| 226 | return {accelerometer * accel_magnitude, gyroscope * gyro_magnitude, rotation, orientation, | ||
| 227 | quaternion}; | ||
| 228 | } | ||
| 229 | |||
| 230 | void MotionInput::ResetOrientation() { | 203 | void MotionInput::ResetOrientation() { |
| 231 | if (!reset_enabled || only_accelerometer) { | 204 | if (!reset_enabled || only_accelerometer) { |
| 232 | return; | 205 | return; |
| @@ -304,4 +277,4 @@ void MotionInput::SetOrientationFromAccelerometer() { | |||
| 304 | quat = quat.Normalized(); | 277 | quat = quat.Normalized(); |
| 305 | } | 278 | } |
| 306 | } | 279 | } |
| 307 | } // namespace InputCommon | 280 | } // namespace Core::HID |
diff --git a/src/input_common/motion_input.h b/src/core/hid/motion_input.h index efe74cf19..5b5b420bb 100644 --- a/src/input_common/motion_input.h +++ b/src/core/hid/motion_input.h | |||
| @@ -7,13 +7,12 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/quaternion.h" | 8 | #include "common/quaternion.h" |
| 9 | #include "common/vector_math.h" | 9 | #include "common/vector_math.h" |
| 10 | #include "core/frontend/input.h" | ||
| 11 | 10 | ||
| 12 | namespace InputCommon { | 11 | namespace Core::HID { |
| 13 | 12 | ||
| 14 | class MotionInput { | 13 | class MotionInput { |
| 15 | public: | 14 | public: |
| 16 | explicit MotionInput(f32 new_kp, f32 new_ki, f32 new_kd); | 15 | explicit MotionInput(); |
| 17 | 16 | ||
| 18 | MotionInput(const MotionInput&) = default; | 17 | MotionInput(const MotionInput&) = default; |
| 19 | MotionInput& operator=(const MotionInput&) = default; | 18 | MotionInput& operator=(const MotionInput&) = default; |
| @@ -21,6 +20,7 @@ public: | |||
| 21 | MotionInput(MotionInput&&) = default; | 20 | MotionInput(MotionInput&&) = default; |
| 22 | MotionInput& operator=(MotionInput&&) = default; | 21 | MotionInput& operator=(MotionInput&&) = default; |
| 23 | 22 | ||
| 23 | void SetPID(f32 new_kp, f32 new_ki, f32 new_kd); | ||
| 24 | void SetAcceleration(const Common::Vec3f& acceleration); | 24 | void SetAcceleration(const Common::Vec3f& acceleration); |
| 25 | void SetGyroscope(const Common::Vec3f& gyroscope); | 25 | void SetGyroscope(const Common::Vec3f& gyroscope); |
| 26 | void SetQuaternion(const Common::Quaternion<f32>& quaternion); | 26 | void SetQuaternion(const Common::Quaternion<f32>& quaternion); |
| @@ -38,9 +38,6 @@ public: | |||
| 38 | [[nodiscard]] Common::Vec3f GetGyroscope() const; | 38 | [[nodiscard]] Common::Vec3f GetGyroscope() const; |
| 39 | [[nodiscard]] Common::Vec3f GetRotations() const; | 39 | [[nodiscard]] Common::Vec3f GetRotations() const; |
| 40 | [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; | 40 | [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; |
| 41 | [[nodiscard]] Input::MotionStatus GetMotion() const; | ||
| 42 | [[nodiscard]] Input::MotionStatus GetRandomMotion(int accel_magnitude, | ||
| 43 | int gyro_magnitude) const; | ||
| 44 | 41 | ||
| 45 | [[nodiscard]] bool IsMoving(f32 sensitivity) const; | 42 | [[nodiscard]] bool IsMoving(f32 sensitivity) const; |
| 46 | [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; | 43 | [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; |
| @@ -59,16 +56,32 @@ private: | |||
| 59 | Common::Vec3f integral_error; | 56 | Common::Vec3f integral_error; |
| 60 | Common::Vec3f derivative_error; | 57 | Common::Vec3f derivative_error; |
| 61 | 58 | ||
| 59 | // Quaternion containing the device orientation | ||
| 62 | Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f}; | 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; | 63 | Common::Vec3f rotations; |
| 64 | |||
| 65 | // Acceleration vector measurement in G force | ||
| 64 | Common::Vec3f accel; | 66 | Common::Vec3f accel; |
| 67 | |||
| 68 | // Gyroscope vector measurement in radians/s. | ||
| 65 | Common::Vec3f gyro; | 69 | Common::Vec3f gyro; |
| 70 | |||
| 71 | // Vector to be substracted from gyro measurements | ||
| 66 | Common::Vec3f gyro_drift; | 72 | Common::Vec3f gyro_drift; |
| 67 | 73 | ||
| 74 | // Minimum gyro amplitude to detect if the device is moving | ||
| 68 | f32 gyro_threshold = 0.0f; | 75 | f32 gyro_threshold = 0.0f; |
| 76 | |||
| 77 | // Number of invalid sequential data | ||
| 69 | u32 reset_counter = 0; | 78 | u32 reset_counter = 0; |
| 79 | |||
| 80 | // If the provided data is invalid the device will be autocalibrated | ||
| 70 | bool reset_enabled = true; | 81 | bool reset_enabled = true; |
| 82 | |||
| 83 | // Use accelerometer values to calculate position | ||
| 71 | bool only_accelerometer = true; | 84 | bool only_accelerometer = true; |
| 72 | }; | 85 | }; |
| 73 | 86 | ||
| 74 | } // namespace InputCommon | 87 | } // namespace Core::HID |
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index 8ff0f695d..36fc0944a 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hardware_properties.h" | 10 | #include "core/hardware_properties.h" |
| 11 | #include "core/hle/kernel/init/init_slab_setup.h" | 11 | #include "core/hle/kernel/init/init_slab_setup.h" |
| 12 | #include "core/hle/kernel/k_code_memory.h" | ||
| 12 | #include "core/hle/kernel/k_event.h" | 13 | #include "core/hle/kernel/k_event.h" |
| 13 | #include "core/hle/kernel/k_memory_layout.h" | 14 | #include "core/hle/kernel/k_memory_layout.h" |
| 14 | #include "core/hle/kernel/k_memory_manager.h" | 15 | #include "core/hle/kernel/k_memory_manager.h" |
| @@ -32,6 +33,7 @@ namespace Kernel::Init { | |||
| 32 | HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ | 33 | HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ |
| 33 | HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ | 34 | HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ |
| 34 | HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ | 35 | HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ |
| 36 | HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ | ||
| 35 | HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ | 37 | HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ |
| 36 | HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) | 38 | HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) |
| 37 | 39 | ||
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index 1b429bc1e..783c69858 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "core/hle/kernel/k_scheduler.h" | 8 | #include "core/hle/kernel/k_scheduler.h" |
| 9 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | 9 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" |
| 10 | #include "core/hle/kernel/k_thread.h" | 10 | #include "core/hle/kernel/k_thread.h" |
| 11 | #include "core/hle/kernel/k_thread_queue.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | 12 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/svc_results.h" | 13 | #include "core/hle/kernel/svc_results.h" |
| 13 | #include "core/hle/kernel/time_manager.h" | 14 | #include "core/hle/kernel/time_manager.h" |
| @@ -28,7 +29,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) { | |||
| 28 | 29 | ||
| 29 | bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { | 30 | bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { |
| 30 | auto& monitor = system.Monitor(); | 31 | auto& monitor = system.Monitor(); |
| 31 | const auto current_core = system.CurrentCoreIndex(); | 32 | const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); |
| 32 | 33 | ||
| 33 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. | 34 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. |
| 34 | // TODO(bunnei): We should call CanAccessAtomic(..) here. | 35 | // TODO(bunnei): We should call CanAccessAtomic(..) here. |
| @@ -58,7 +59,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu | |||
| 58 | 59 | ||
| 59 | bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { | 60 | bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { |
| 60 | auto& monitor = system.Monitor(); | 61 | auto& monitor = system.Monitor(); |
| 61 | const auto current_core = system.CurrentCoreIndex(); | 62 | const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); |
| 62 | 63 | ||
| 63 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. | 64 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. |
| 64 | // TODO(bunnei): We should call CanAccessAtomic(..) here. | 65 | // TODO(bunnei): We should call CanAccessAtomic(..) here. |
| @@ -85,6 +86,27 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 | |||
| 85 | return true; | 86 | return true; |
| 86 | } | 87 | } |
| 87 | 88 | ||
| 89 | class ThreadQueueImplForKAddressArbiter final : public KThreadQueue { | ||
| 90 | public: | ||
| 91 | explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t) | ||
| 92 | : KThreadQueue(kernel_), m_tree(t) {} | ||
| 93 | |||
| 94 | void CancelWait(KThread* waiting_thread, ResultCode wait_result, | ||
| 95 | bool cancel_timer_task) override { | ||
| 96 | // If the thread is waiting on an address arbiter, remove it from the tree. | ||
| 97 | if (waiting_thread->IsWaitingForAddressArbiter()) { | ||
| 98 | m_tree->erase(m_tree->iterator_to(*waiting_thread)); | ||
| 99 | waiting_thread->ClearAddressArbiter(); | ||
| 100 | } | ||
| 101 | |||
| 102 | // Invoke the base cancel wait handler. | ||
| 103 | KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); | ||
| 104 | } | ||
| 105 | |||
| 106 | private: | ||
| 107 | KAddressArbiter::ThreadTree* m_tree; | ||
| 108 | }; | ||
| 109 | |||
| 88 | } // namespace | 110 | } // namespace |
| 89 | 111 | ||
| 90 | ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { | 112 | ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { |
| @@ -96,14 +118,14 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { | |||
| 96 | auto it = thread_tree.nfind_light({addr, -1}); | 118 | auto it = thread_tree.nfind_light({addr, -1}); |
| 97 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | 119 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && |
| 98 | (it->GetAddressArbiterKey() == addr)) { | 120 | (it->GetAddressArbiterKey() == addr)) { |
| 121 | // End the thread's wait. | ||
| 99 | KThread* target_thread = std::addressof(*it); | 122 | KThread* target_thread = std::addressof(*it); |
| 100 | target_thread->SetSyncedObject(nullptr, ResultSuccess); | 123 | target_thread->EndWait(ResultSuccess); |
| 101 | 124 | ||
| 102 | ASSERT(target_thread->IsWaitingForAddressArbiter()); | 125 | ASSERT(target_thread->IsWaitingForAddressArbiter()); |
| 103 | target_thread->Wakeup(); | 126 | target_thread->ClearAddressArbiter(); |
| 104 | 127 | ||
| 105 | it = thread_tree.erase(it); | 128 | it = thread_tree.erase(it); |
| 106 | target_thread->ClearAddressArbiter(); | ||
| 107 | ++num_waiters; | 129 | ++num_waiters; |
| 108 | } | 130 | } |
| 109 | } | 131 | } |
| @@ -129,14 +151,14 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 | |||
| 129 | auto it = thread_tree.nfind_light({addr, -1}); | 151 | auto it = thread_tree.nfind_light({addr, -1}); |
| 130 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | 152 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && |
| 131 | (it->GetAddressArbiterKey() == addr)) { | 153 | (it->GetAddressArbiterKey() == addr)) { |
| 154 | // End the thread's wait. | ||
| 132 | KThread* target_thread = std::addressof(*it); | 155 | KThread* target_thread = std::addressof(*it); |
| 133 | target_thread->SetSyncedObject(nullptr, ResultSuccess); | 156 | target_thread->EndWait(ResultSuccess); |
| 134 | 157 | ||
| 135 | ASSERT(target_thread->IsWaitingForAddressArbiter()); | 158 | ASSERT(target_thread->IsWaitingForAddressArbiter()); |
| 136 | target_thread->Wakeup(); | 159 | target_thread->ClearAddressArbiter(); |
| 137 | 160 | ||
| 138 | it = thread_tree.erase(it); | 161 | it = thread_tree.erase(it); |
| 139 | target_thread->ClearAddressArbiter(); | ||
| 140 | ++num_waiters; | 162 | ++num_waiters; |
| 141 | } | 163 | } |
| 142 | } | 164 | } |
| @@ -197,14 +219,14 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 | |||
| 197 | 219 | ||
| 198 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | 220 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && |
| 199 | (it->GetAddressArbiterKey() == addr)) { | 221 | (it->GetAddressArbiterKey() == addr)) { |
| 222 | // End the thread's wait. | ||
| 200 | KThread* target_thread = std::addressof(*it); | 223 | KThread* target_thread = std::addressof(*it); |
| 201 | target_thread->SetSyncedObject(nullptr, ResultSuccess); | 224 | target_thread->EndWait(ResultSuccess); |
| 202 | 225 | ||
| 203 | ASSERT(target_thread->IsWaitingForAddressArbiter()); | 226 | ASSERT(target_thread->IsWaitingForAddressArbiter()); |
| 204 | target_thread->Wakeup(); | 227 | target_thread->ClearAddressArbiter(); |
| 205 | 228 | ||
| 206 | it = thread_tree.erase(it); | 229 | it = thread_tree.erase(it); |
| 207 | target_thread->ClearAddressArbiter(); | ||
| 208 | ++num_waiters; | 230 | ++num_waiters; |
| 209 | } | 231 | } |
| 210 | } | 232 | } |
| @@ -214,6 +236,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 | |||
| 214 | ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { | 236 | ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { |
| 215 | // Prepare to wait. | 237 | // Prepare to wait. |
| 216 | KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); | 238 | KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); |
| 239 | ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree)); | ||
| 217 | 240 | ||
| 218 | { | 241 | { |
| 219 | KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; | 242 | KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; |
| @@ -224,9 +247,6 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement | |||
| 224 | return ResultTerminationRequested; | 247 | return ResultTerminationRequested; |
| 225 | } | 248 | } |
| 226 | 249 | ||
| 227 | // Set the synced object. | ||
| 228 | cur_thread->SetSyncedObject(nullptr, ResultTimedOut); | ||
| 229 | |||
| 230 | // Read the value from userspace. | 250 | // Read the value from userspace. |
| 231 | s32 user_value{}; | 251 | s32 user_value{}; |
| 232 | bool succeeded{}; | 252 | bool succeeded{}; |
| @@ -256,31 +276,20 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement | |||
| 256 | // Set the arbiter. | 276 | // Set the arbiter. |
| 257 | cur_thread->SetAddressArbiter(&thread_tree, addr); | 277 | cur_thread->SetAddressArbiter(&thread_tree, addr); |
| 258 | thread_tree.insert(*cur_thread); | 278 | thread_tree.insert(*cur_thread); |
| 259 | cur_thread->SetState(ThreadState::Waiting); | ||
| 260 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); | ||
| 261 | } | ||
| 262 | |||
| 263 | // Cancel the timer wait. | ||
| 264 | kernel.TimeManager().UnscheduleTimeEvent(cur_thread); | ||
| 265 | 279 | ||
| 266 | // Remove from the address arbiter. | 280 | // Wait for the thread to finish. |
| 267 | { | 281 | cur_thread->BeginWait(std::addressof(wait_queue)); |
| 268 | KScopedSchedulerLock sl(kernel); | 282 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); |
| 269 | |||
| 270 | if (cur_thread->IsWaitingForAddressArbiter()) { | ||
| 271 | thread_tree.erase(thread_tree.iterator_to(*cur_thread)); | ||
| 272 | cur_thread->ClearAddressArbiter(); | ||
| 273 | } | ||
| 274 | } | 283 | } |
| 275 | 284 | ||
| 276 | // Get the result. | 285 | // Get the result. |
| 277 | KSynchronizationObject* dummy{}; | 286 | return cur_thread->GetWaitResult(); |
| 278 | return cur_thread->GetWaitResult(&dummy); | ||
| 279 | } | 287 | } |
| 280 | 288 | ||
| 281 | ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { | 289 | ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { |
| 282 | // Prepare to wait. | 290 | // Prepare to wait. |
| 283 | KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); | 291 | KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); |
| 292 | ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree)); | ||
| 284 | 293 | ||
| 285 | { | 294 | { |
| 286 | KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; | 295 | KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; |
| @@ -291,9 +300,6 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { | |||
| 291 | return ResultTerminationRequested; | 300 | return ResultTerminationRequested; |
| 292 | } | 301 | } |
| 293 | 302 | ||
| 294 | // Set the synced object. | ||
| 295 | cur_thread->SetSyncedObject(nullptr, ResultTimedOut); | ||
| 296 | |||
| 297 | // Read the value from userspace. | 303 | // Read the value from userspace. |
| 298 | s32 user_value{}; | 304 | s32 user_value{}; |
| 299 | if (!ReadFromUser(system, &user_value, addr)) { | 305 | if (!ReadFromUser(system, &user_value, addr)) { |
| @@ -316,26 +322,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { | |||
| 316 | // Set the arbiter. | 322 | // Set the arbiter. |
| 317 | cur_thread->SetAddressArbiter(&thread_tree, addr); | 323 | cur_thread->SetAddressArbiter(&thread_tree, addr); |
| 318 | thread_tree.insert(*cur_thread); | 324 | thread_tree.insert(*cur_thread); |
| 319 | cur_thread->SetState(ThreadState::Waiting); | ||
| 320 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); | ||
| 321 | } | ||
| 322 | |||
| 323 | // Cancel the timer wait. | ||
| 324 | kernel.TimeManager().UnscheduleTimeEvent(cur_thread); | ||
| 325 | 325 | ||
| 326 | // Remove from the address arbiter. | 326 | // Wait for the thread to finish. |
| 327 | { | 327 | cur_thread->BeginWait(std::addressof(wait_queue)); |
| 328 | KScopedSchedulerLock sl(kernel); | 328 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); |
| 329 | |||
| 330 | if (cur_thread->IsWaitingForAddressArbiter()) { | ||
| 331 | thread_tree.erase(thread_tree.iterator_to(*cur_thread)); | ||
| 332 | cur_thread->ClearAddressArbiter(); | ||
| 333 | } | ||
| 334 | } | 329 | } |
| 335 | 330 | ||
| 336 | // Get the result. | 331 | // Get the result. |
| 337 | KSynchronizationObject* dummy{}; | 332 | return cur_thread->GetWaitResult(); |
| 338 | return cur_thread->GetWaitResult(&dummy); | ||
| 339 | } | 333 | } |
| 340 | 334 | ||
| 341 | } // namespace Kernel | 335 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index e4fcdbc67..165b76747 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h | |||
| @@ -170,6 +170,10 @@ public: | |||
| 170 | } | 170 | } |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | const std::string& GetName() const { | ||
| 174 | return name; | ||
| 175 | } | ||
| 176 | |||
| 173 | private: | 177 | private: |
| 174 | void RegisterWithKernel(); | 178 | void RegisterWithKernel(); |
| 175 | void UnregisterWithKernel(); | 179 | void UnregisterWithKernel(); |
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp index 0be0027be..21e2fe494 100644 --- a/src/core/hle/kernel/k_class_token.cpp +++ b/src/core/hle/kernel/k_class_token.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "core/hle/kernel/k_class_token.h" | 6 | #include "core/hle/kernel/k_class_token.h" |
| 7 | #include "core/hle/kernel/k_client_port.h" | 7 | #include "core/hle/kernel/k_client_port.h" |
| 8 | #include "core/hle/kernel/k_client_session.h" | 8 | #include "core/hle/kernel/k_client_session.h" |
| 9 | #include "core/hle/kernel/k_code_memory.h" | ||
| 9 | #include "core/hle/kernel/k_event.h" | 10 | #include "core/hle/kernel/k_event.h" |
| 10 | #include "core/hle/kernel/k_port.h" | 11 | #include "core/hle/kernel/k_port.h" |
| 11 | #include "core/hle/kernel/k_process.h" | 12 | #include "core/hle/kernel/k_process.h" |
| @@ -48,7 +49,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000); | |||
| 48 | static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000); | 49 | static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000); |
| 49 | // static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000); | 50 | // static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000); |
| 50 | // static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000); | 51 | // static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000); |
| 51 | // static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000); | 52 | static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000); |
| 52 | 53 | ||
| 53 | // Ensure that the token hierarchy is correct. | 54 | // Ensure that the token hierarchy is correct. |
| 54 | 55 | ||
| @@ -79,7 +80,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut | |||
| 79 | static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>)); | 80 | static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>)); |
| 80 | // static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>)); | 81 | // static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>)); |
| 81 | // static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>)); | 82 | // static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>)); |
| 82 | // static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>)); | 83 | static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>)); |
| 83 | 84 | ||
| 84 | // Ensure that the token hierarchy reflects the class hierarchy. | 85 | // Ensure that the token hierarchy reflects the class hierarchy. |
| 85 | 86 | ||
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp new file mode 100644 index 000000000..d69f7ffb7 --- /dev/null +++ b/src/core/hle/kernel/k_code_memory.cpp | |||
| @@ -0,0 +1,146 @@ | |||
| 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/common_types.h" | ||
| 6 | #include "core/device_memory.h" | ||
| 7 | #include "core/hle/kernel/k_auto_object.h" | ||
| 8 | #include "core/hle/kernel/k_code_memory.h" | ||
| 9 | #include "core/hle/kernel/k_light_lock.h" | ||
| 10 | #include "core/hle/kernel/k_memory_block.h" | ||
| 11 | #include "core/hle/kernel/k_page_linked_list.h" | ||
| 12 | #include "core/hle/kernel/k_page_table.h" | ||
| 13 | #include "core/hle/kernel/k_process.h" | ||
| 14 | #include "core/hle/kernel/slab_helpers.h" | ||
| 15 | #include "core/hle/kernel/svc_types.h" | ||
| 16 | #include "core/hle/result.h" | ||
| 17 | |||
| 18 | namespace Kernel { | ||
| 19 | |||
| 20 | KCodeMemory::KCodeMemory(KernelCore& kernel_) | ||
| 21 | : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {} | ||
| 22 | |||
| 23 | ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) { | ||
| 24 | // Set members. | ||
| 25 | m_owner = kernel.CurrentProcess(); | ||
| 26 | |||
| 27 | // Get the owner page table. | ||
| 28 | auto& page_table = m_owner->PageTable(); | ||
| 29 | |||
| 30 | // Construct the page group. | ||
| 31 | KMemoryInfo kBlockInfo = page_table.QueryInfo(addr); | ||
| 32 | m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); | ||
| 33 | |||
| 34 | // Lock the memory. | ||
| 35 | R_TRY(page_table.LockForCodeMemory(addr, size)) | ||
| 36 | |||
| 37 | // Clear the memory. | ||
| 38 | for (const auto& block : m_page_group.Nodes()) { | ||
| 39 | std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); | ||
| 40 | } | ||
| 41 | |||
| 42 | // Set remaining tracking members. | ||
| 43 | m_address = addr; | ||
| 44 | m_is_initialized = true; | ||
| 45 | m_is_owner_mapped = false; | ||
| 46 | m_is_mapped = false; | ||
| 47 | |||
| 48 | // We succeeded. | ||
| 49 | return ResultSuccess; | ||
| 50 | } | ||
| 51 | |||
| 52 | void KCodeMemory::Finalize() { | ||
| 53 | // Unlock. | ||
| 54 | if (!m_is_mapped && !m_is_owner_mapped) { | ||
| 55 | const size_t size = m_page_group.GetNumPages() * PageSize; | ||
| 56 | m_owner->PageTable().UnlockForCodeMemory(m_address, size); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | ResultCode KCodeMemory::Map(VAddr address, size_t size) { | ||
| 61 | // Validate the size. | ||
| 62 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 63 | |||
| 64 | // Lock ourselves. | ||
| 65 | KScopedLightLock lk(m_lock); | ||
| 66 | |||
| 67 | // Ensure we're not already mapped. | ||
| 68 | R_UNLESS(!m_is_mapped, ResultInvalidState); | ||
| 69 | |||
| 70 | // Map the memory. | ||
| 71 | R_TRY(kernel.CurrentProcess()->PageTable().MapPages( | ||
| 72 | address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); | ||
| 73 | |||
| 74 | // Mark ourselves as mapped. | ||
| 75 | m_is_mapped = true; | ||
| 76 | |||
| 77 | return ResultSuccess; | ||
| 78 | } | ||
| 79 | |||
| 80 | ResultCode KCodeMemory::Unmap(VAddr address, size_t size) { | ||
| 81 | // Validate the size. | ||
| 82 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 83 | |||
| 84 | // Lock ourselves. | ||
| 85 | KScopedLightLock lk(m_lock); | ||
| 86 | |||
| 87 | // Unmap the memory. | ||
| 88 | R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group, | ||
| 89 | KMemoryState::CodeOut)); | ||
| 90 | |||
| 91 | // Mark ourselves as unmapped. | ||
| 92 | m_is_mapped = false; | ||
| 93 | |||
| 94 | return ResultSuccess; | ||
| 95 | } | ||
| 96 | |||
| 97 | ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { | ||
| 98 | // Validate the size. | ||
| 99 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 100 | |||
| 101 | // Lock ourselves. | ||
| 102 | KScopedLightLock lk(m_lock); | ||
| 103 | |||
| 104 | // Ensure we're not already mapped. | ||
| 105 | R_UNLESS(!m_is_owner_mapped, ResultInvalidState); | ||
| 106 | |||
| 107 | // Convert the memory permission. | ||
| 108 | KMemoryPermission k_perm{}; | ||
| 109 | switch (perm) { | ||
| 110 | case Svc::MemoryPermission::Read: | ||
| 111 | k_perm = KMemoryPermission::UserRead; | ||
| 112 | break; | ||
| 113 | case Svc::MemoryPermission::ReadExecute: | ||
| 114 | k_perm = KMemoryPermission::UserReadExecute; | ||
| 115 | break; | ||
| 116 | default: | ||
| 117 | break; | ||
| 118 | } | ||
| 119 | |||
| 120 | // Map the memory. | ||
| 121 | R_TRY( | ||
| 122 | m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm)); | ||
| 123 | |||
| 124 | // Mark ourselves as mapped. | ||
| 125 | m_is_owner_mapped = true; | ||
| 126 | |||
| 127 | return ResultSuccess; | ||
| 128 | } | ||
| 129 | |||
| 130 | ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { | ||
| 131 | // Validate the size. | ||
| 132 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 133 | |||
| 134 | // Lock ourselves. | ||
| 135 | KScopedLightLock lk(m_lock); | ||
| 136 | |||
| 137 | // Unmap the memory. | ||
| 138 | R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode)); | ||
| 139 | |||
| 140 | // Mark ourselves as unmapped. | ||
| 141 | m_is_owner_mapped = false; | ||
| 142 | |||
| 143 | return ResultSuccess; | ||
| 144 | } | ||
| 145 | |||
| 146 | } // namespace Kernel \ No newline at end of file | ||
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h new file mode 100644 index 000000000..e0ba19a53 --- /dev/null +++ b/src/core/hle/kernel/k_code_memory.h | |||
| @@ -0,0 +1,66 @@ | |||
| 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/common_types.h" | ||
| 8 | #include "core/device_memory.h" | ||
| 9 | #include "core/hle/kernel/k_auto_object.h" | ||
| 10 | #include "core/hle/kernel/k_light_lock.h" | ||
| 11 | #include "core/hle/kernel/k_page_linked_list.h" | ||
| 12 | #include "core/hle/kernel/k_process.h" | ||
| 13 | #include "core/hle/kernel/slab_helpers.h" | ||
| 14 | #include "core/hle/kernel/svc_types.h" | ||
| 15 | #include "core/hle/result.h" | ||
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | |||
| 19 | enum class CodeMemoryOperation : u32 { | ||
| 20 | Map = 0, | ||
| 21 | MapToOwner = 1, | ||
| 22 | Unmap = 2, | ||
| 23 | UnmapFromOwner = 3, | ||
| 24 | }; | ||
| 25 | |||
| 26 | class KCodeMemory final | ||
| 27 | : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> { | ||
| 28 | KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject); | ||
| 29 | |||
| 30 | public: | ||
| 31 | explicit KCodeMemory(KernelCore& kernel_); | ||
| 32 | |||
| 33 | ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); | ||
| 34 | void Finalize(); | ||
| 35 | |||
| 36 | ResultCode Map(VAddr address, size_t size); | ||
| 37 | ResultCode Unmap(VAddr address, size_t size); | ||
| 38 | ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); | ||
| 39 | ResultCode UnmapFromOwner(VAddr address, size_t size); | ||
| 40 | |||
| 41 | bool IsInitialized() const { | ||
| 42 | return m_is_initialized; | ||
| 43 | } | ||
| 44 | static void PostDestroy([[maybe_unused]] uintptr_t arg) {} | ||
| 45 | |||
| 46 | KProcess* GetOwner() const { | ||
| 47 | return m_owner; | ||
| 48 | } | ||
| 49 | VAddr GetSourceAddress() const { | ||
| 50 | return m_address; | ||
| 51 | } | ||
| 52 | size_t GetSize() const { | ||
| 53 | return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | private: | ||
| 57 | KPageLinkedList m_page_group{}; | ||
| 58 | KProcess* m_owner{}; | ||
| 59 | VAddr m_address{}; | ||
| 60 | KLightLock m_lock; | ||
| 61 | bool m_is_initialized{}; | ||
| 62 | bool m_is_owner_mapped{}; | ||
| 63 | bool m_is_mapped{}; | ||
| 64 | }; | ||
| 65 | |||
| 66 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index 7fa9b8cc3..aadcc297a 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | 11 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" |
| 12 | #include "core/hle/kernel/k_synchronization_object.h" | 12 | #include "core/hle/kernel/k_synchronization_object.h" |
| 13 | #include "core/hle/kernel/k_thread.h" | 13 | #include "core/hle/kernel/k_thread.h" |
| 14 | #include "core/hle/kernel/k_thread_queue.h" | ||
| 14 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| 15 | #include "core/hle/kernel/svc_common.h" | 16 | #include "core/hle/kernel/svc_common.h" |
| 16 | #include "core/hle/kernel/svc_results.h" | 17 | #include "core/hle/kernel/svc_results.h" |
| @@ -33,7 +34,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) { | |||
| 33 | bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, | 34 | bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, |
| 34 | u32 new_orr_mask) { | 35 | u32 new_orr_mask) { |
| 35 | auto& monitor = system.Monitor(); | 36 | auto& monitor = system.Monitor(); |
| 36 | const auto current_core = system.CurrentCoreIndex(); | 37 | const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); |
| 37 | 38 | ||
| 38 | // Load the value from the address. | 39 | // Load the value from the address. |
| 39 | const auto expected = monitor.ExclusiveRead32(current_core, address); | 40 | const auto expected = monitor.ExclusiveRead32(current_core, address); |
| @@ -57,6 +58,48 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero | |||
| 57 | return true; | 58 | return true; |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 61 | class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue { | ||
| 62 | public: | ||
| 63 | explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_) | ||
| 64 | : KThreadQueue(kernel_) {} | ||
| 65 | |||
| 66 | void CancelWait(KThread* waiting_thread, ResultCode wait_result, | ||
| 67 | bool cancel_timer_task) override { | ||
| 68 | // Remove the thread as a waiter from its owner. | ||
| 69 | waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread); | ||
| 70 | |||
| 71 | // Invoke the base cancel wait handler. | ||
| 72 | KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); | ||
| 73 | } | ||
| 74 | }; | ||
| 75 | |||
| 76 | class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue { | ||
| 77 | private: | ||
| 78 | KConditionVariable::ThreadTree* m_tree; | ||
| 79 | |||
| 80 | public: | ||
| 81 | explicit ThreadQueueImplForKConditionVariableWaitConditionVariable( | ||
| 82 | KernelCore& kernel_, KConditionVariable::ThreadTree* t) | ||
| 83 | : KThreadQueue(kernel_), m_tree(t) {} | ||
| 84 | |||
| 85 | void CancelWait(KThread* waiting_thread, ResultCode wait_result, | ||
| 86 | bool cancel_timer_task) override { | ||
| 87 | // Remove the thread as a waiter from its owner. | ||
| 88 | if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) { | ||
| 89 | owner->RemoveWaiter(waiting_thread); | ||
| 90 | } | ||
| 91 | |||
| 92 | // If the thread is waiting on a condvar, remove it from the tree. | ||
| 93 | if (waiting_thread->IsWaitingForConditionVariable()) { | ||
| 94 | m_tree->erase(m_tree->iterator_to(*waiting_thread)); | ||
| 95 | waiting_thread->ClearConditionVariable(); | ||
| 96 | } | ||
| 97 | |||
| 98 | // Invoke the base cancel wait handler. | ||
| 99 | KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); | ||
| 100 | } | ||
| 101 | }; | ||
| 102 | |||
| 60 | } // namespace | 103 | } // namespace |
| 61 | 104 | ||
| 62 | KConditionVariable::KConditionVariable(Core::System& system_) | 105 | KConditionVariable::KConditionVariable(Core::System& system_) |
| @@ -78,84 +121,77 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { | |||
| 78 | 121 | ||
| 79 | // Determine the next tag. | 122 | // Determine the next tag. |
| 80 | u32 next_value{}; | 123 | u32 next_value{}; |
| 81 | if (next_owner_thread) { | 124 | if (next_owner_thread != nullptr) { |
| 82 | next_value = next_owner_thread->GetAddressKeyValue(); | 125 | next_value = next_owner_thread->GetAddressKeyValue(); |
| 83 | if (num_waiters > 1) { | 126 | if (num_waiters > 1) { |
| 84 | next_value |= Svc::HandleWaitMask; | 127 | next_value |= Svc::HandleWaitMask; |
| 85 | } | 128 | } |
| 86 | 129 | ||
| 87 | next_owner_thread->SetSyncedObject(nullptr, ResultSuccess); | 130 | // Write the value to userspace. |
| 88 | next_owner_thread->Wakeup(); | 131 | ResultCode result{ResultSuccess}; |
| 89 | } | 132 | if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] { |
| 90 | 133 | result = ResultSuccess; | |
| 91 | // Write the value to userspace. | 134 | } else { |
| 92 | if (!WriteToUser(system, addr, std::addressof(next_value))) { | 135 | result = ResultInvalidCurrentMemory; |
| 93 | if (next_owner_thread) { | ||
| 94 | next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory); | ||
| 95 | } | 136 | } |
| 96 | 137 | ||
| 97 | return ResultInvalidCurrentMemory; | 138 | // Signal the next owner thread. |
| 139 | next_owner_thread->EndWait(result); | ||
| 140 | return result; | ||
| 141 | } else { | ||
| 142 | // Just write the value to userspace. | ||
| 143 | R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)), | ||
| 144 | ResultInvalidCurrentMemory); | ||
| 145 | |||
| 146 | return ResultSuccess; | ||
| 98 | } | 147 | } |
| 99 | } | 148 | } |
| 100 | |||
| 101 | return ResultSuccess; | ||
| 102 | } | 149 | } |
| 103 | 150 | ||
| 104 | ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { | 151 | ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { |
| 105 | KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); | 152 | KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); |
| 153 | ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel); | ||
| 106 | 154 | ||
| 107 | // Wait for the address. | 155 | // Wait for the address. |
| 156 | KThread* owner_thread{}; | ||
| 108 | { | 157 | { |
| 109 | KScopedAutoObject<KThread> owner_thread; | 158 | KScopedSchedulerLock sl(kernel); |
| 110 | ASSERT(owner_thread.IsNull()); | ||
| 111 | { | ||
| 112 | KScopedSchedulerLock sl(kernel); | ||
| 113 | cur_thread->SetSyncedObject(nullptr, ResultSuccess); | ||
| 114 | 159 | ||
| 115 | // Check if the thread should terminate. | 160 | // Check if the thread should terminate. |
| 116 | R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested); | 161 | R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested); |
| 117 | 162 | ||
| 118 | { | 163 | // Read the tag from userspace. |
| 119 | // Read the tag from userspace. | 164 | u32 test_tag{}; |
| 120 | u32 test_tag{}; | 165 | R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory); |
| 121 | R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), | ||
| 122 | ResultInvalidCurrentMemory); | ||
| 123 | |||
| 124 | // If the tag isn't the handle (with wait mask), we're done. | ||
| 125 | R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess); | ||
| 126 | |||
| 127 | // Get the lock owner thread. | ||
| 128 | owner_thread = | ||
| 129 | kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>( | ||
| 130 | handle); | ||
| 131 | R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle); | ||
| 132 | |||
| 133 | // Update the lock. | ||
| 134 | cur_thread->SetAddressKey(addr, value); | ||
| 135 | owner_thread->AddWaiter(cur_thread); | ||
| 136 | cur_thread->SetState(ThreadState::Waiting); | ||
| 137 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); | ||
| 138 | cur_thread->SetMutexWaitAddressForDebugging(addr); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | ASSERT(owner_thread.IsNotNull()); | ||
| 142 | } | ||
| 143 | 166 | ||
| 144 | // Remove the thread as a waiter from the lock owner. | 167 | // If the tag isn't the handle (with wait mask), we're done. |
| 145 | { | 168 | R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask)); |
| 146 | KScopedSchedulerLock sl(kernel); | 169 | |
| 147 | KThread* owner_thread = cur_thread->GetLockOwner(); | 170 | // Get the lock owner thread. |
| 148 | if (owner_thread != nullptr) { | 171 | owner_thread = kernel.CurrentProcess() |
| 149 | owner_thread->RemoveWaiter(cur_thread); | 172 | ->GetHandleTable() |
| 150 | } | 173 | .GetObjectWithoutPseudoHandle<KThread>(handle) |
| 174 | .ReleasePointerUnsafe(); | ||
| 175 | R_UNLESS(owner_thread != nullptr, ResultInvalidHandle); | ||
| 176 | |||
| 177 | // Update the lock. | ||
| 178 | cur_thread->SetAddressKey(addr, value); | ||
| 179 | owner_thread->AddWaiter(cur_thread); | ||
| 180 | |||
| 181 | // Begin waiting. | ||
| 182 | cur_thread->BeginWait(std::addressof(wait_queue)); | ||
| 183 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); | ||
| 184 | cur_thread->SetMutexWaitAddressForDebugging(addr); | ||
| 151 | } | 185 | } |
| 152 | 186 | ||
| 187 | // Close our reference to the owner thread, now that the wait is over. | ||
| 188 | owner_thread->Close(); | ||
| 189 | |||
| 153 | // Get the wait result. | 190 | // Get the wait result. |
| 154 | KSynchronizationObject* dummy{}; | 191 | return cur_thread->GetWaitResult(); |
| 155 | return cur_thread->GetWaitResult(std::addressof(dummy)); | ||
| 156 | } | 192 | } |
| 157 | 193 | ||
| 158 | KThread* KConditionVariable::SignalImpl(KThread* thread) { | 194 | void KConditionVariable::SignalImpl(KThread* thread) { |
| 159 | // Check pre-conditions. | 195 | // Check pre-conditions. |
| 160 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 196 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 161 | 197 | ||
| @@ -169,18 +205,16 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) { | |||
| 169 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. | 205 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. |
| 170 | // TODO(bunnei): We should call CanAccessAtomic(..) here. | 206 | // TODO(bunnei): We should call CanAccessAtomic(..) here. |
| 171 | can_access = true; | 207 | can_access = true; |
| 172 | if (can_access) { | 208 | if (can_access) [[likely]] { |
| 173 | UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag, | 209 | UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag, |
| 174 | Svc::HandleWaitMask); | 210 | Svc::HandleWaitMask); |
| 175 | } | 211 | } |
| 176 | } | 212 | } |
| 177 | 213 | ||
| 178 | KThread* thread_to_close = nullptr; | 214 | if (can_access) [[likely]] { |
| 179 | if (can_access) { | ||
| 180 | if (prev_tag == Svc::InvalidHandle) { | 215 | if (prev_tag == Svc::InvalidHandle) { |
| 181 | // If nobody held the lock previously, we're all good. | 216 | // If nobody held the lock previously, we're all good. |
| 182 | thread->SetSyncedObject(nullptr, ResultSuccess); | 217 | thread->EndWait(ResultSuccess); |
| 183 | thread->Wakeup(); | ||
| 184 | } else { | 218 | } else { |
| 185 | // Get the previous owner. | 219 | // Get the previous owner. |
| 186 | KThread* owner_thread = kernel.CurrentProcess() | 220 | KThread* owner_thread = kernel.CurrentProcess() |
| @@ -189,33 +223,22 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) { | |||
| 189 | static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask)) | 223 | static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask)) |
| 190 | .ReleasePointerUnsafe(); | 224 | .ReleasePointerUnsafe(); |
| 191 | 225 | ||
| 192 | if (owner_thread) { | 226 | if (owner_thread) [[likely]] { |
| 193 | // Add the thread as a waiter on the owner. | 227 | // Add the thread as a waiter on the owner. |
| 194 | owner_thread->AddWaiter(thread); | 228 | owner_thread->AddWaiter(thread); |
| 195 | thread_to_close = owner_thread; | 229 | owner_thread->Close(); |
| 196 | } else { | 230 | } else { |
| 197 | // The lock was tagged with a thread that doesn't exist. | 231 | // The lock was tagged with a thread that doesn't exist. |
| 198 | thread->SetSyncedObject(nullptr, ResultInvalidState); | 232 | thread->EndWait(ResultInvalidState); |
| 199 | thread->Wakeup(); | ||
| 200 | } | 233 | } |
| 201 | } | 234 | } |
| 202 | } else { | 235 | } else { |
| 203 | // If the address wasn't accessible, note so. | 236 | // If the address wasn't accessible, note so. |
| 204 | thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory); | 237 | thread->EndWait(ResultInvalidCurrentMemory); |
| 205 | thread->Wakeup(); | ||
| 206 | } | 238 | } |
| 207 | |||
| 208 | return thread_to_close; | ||
| 209 | } | 239 | } |
| 210 | 240 | ||
| 211 | void KConditionVariable::Signal(u64 cv_key, s32 count) { | 241 | void KConditionVariable::Signal(u64 cv_key, s32 count) { |
| 212 | // Prepare for signaling. | ||
| 213 | constexpr int MaxThreads = 16; | ||
| 214 | |||
| 215 | KLinkedList<KThread> thread_list{kernel}; | ||
| 216 | std::array<KThread*, MaxThreads> thread_array; | ||
| 217 | s32 num_to_close{}; | ||
| 218 | |||
| 219 | // Perform signaling. | 242 | // Perform signaling. |
| 220 | s32 num_waiters{}; | 243 | s32 num_waiters{}; |
| 221 | { | 244 | { |
| @@ -226,14 +249,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { | |||
| 226 | (it->GetConditionVariableKey() == cv_key)) { | 249 | (it->GetConditionVariableKey() == cv_key)) { |
| 227 | KThread* target_thread = std::addressof(*it); | 250 | KThread* target_thread = std::addressof(*it); |
| 228 | 251 | ||
| 229 | if (KThread* thread = SignalImpl(target_thread); thread != nullptr) { | 252 | this->SignalImpl(target_thread); |
| 230 | if (num_to_close < MaxThreads) { | ||
| 231 | thread_array[num_to_close++] = thread; | ||
| 232 | } else { | ||
| 233 | thread_list.push_back(*thread); | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | it = thread_tree.erase(it); | 253 | it = thread_tree.erase(it); |
| 238 | target_thread->ClearConditionVariable(); | 254 | target_thread->ClearConditionVariable(); |
| 239 | ++num_waiters; | 255 | ++num_waiters; |
| @@ -245,27 +261,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { | |||
| 245 | WriteToUser(system, cv_key, std::addressof(has_waiter_flag)); | 261 | WriteToUser(system, cv_key, std::addressof(has_waiter_flag)); |
| 246 | } | 262 | } |
| 247 | } | 263 | } |
| 248 | |||
| 249 | // Close threads in the array. | ||
| 250 | for (auto i = 0; i < num_to_close; ++i) { | ||
| 251 | thread_array[i]->Close(); | ||
| 252 | } | ||
| 253 | |||
| 254 | // Close threads in the list. | ||
| 255 | for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) { | ||
| 256 | (*it).Close(); | ||
| 257 | } | ||
| 258 | } | 264 | } |
| 259 | 265 | ||
| 260 | ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { | 266 | ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { |
| 261 | // Prepare to wait. | 267 | // Prepare to wait. |
| 262 | KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); | 268 | KThread* cur_thread = GetCurrentThreadPointer(kernel); |
| 269 | ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue( | ||
| 270 | kernel, std::addressof(thread_tree)); | ||
| 263 | 271 | ||
| 264 | { | 272 | { |
| 265 | KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; | 273 | KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout); |
| 266 | |||
| 267 | // Set the synced object. | ||
| 268 | cur_thread->SetSyncedObject(nullptr, ResultTimedOut); | ||
| 269 | 274 | ||
| 270 | // Check that the thread isn't terminating. | 275 | // Check that the thread isn't terminating. |
| 271 | if (cur_thread->IsTerminationRequested()) { | 276 | if (cur_thread->IsTerminationRequested()) { |
| @@ -290,8 +295,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) | |||
| 290 | } | 295 | } |
| 291 | 296 | ||
| 292 | // Wake up the next owner. | 297 | // Wake up the next owner. |
| 293 | next_owner_thread->SetSyncedObject(nullptr, ResultSuccess); | 298 | next_owner_thread->EndWait(ResultSuccess); |
| 294 | next_owner_thread->Wakeup(); | ||
| 295 | } | 299 | } |
| 296 | 300 | ||
| 297 | // Write to the cv key. | 301 | // Write to the cv key. |
| @@ -308,40 +312,21 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) | |||
| 308 | } | 312 | } |
| 309 | } | 313 | } |
| 310 | 314 | ||
| 311 | // Update condition variable tracking. | 315 | // If timeout is zero, time out. |
| 312 | { | 316 | R_UNLESS(timeout != 0, ResultTimedOut); |
| 313 | cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value); | ||
| 314 | thread_tree.insert(*cur_thread); | ||
| 315 | } | ||
| 316 | 317 | ||
| 317 | // If the timeout is non-zero, set the thread as waiting. | 318 | // Update condition variable tracking. |
| 318 | if (timeout != 0) { | 319 | cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value); |
| 319 | cur_thread->SetState(ThreadState::Waiting); | 320 | thread_tree.insert(*cur_thread); |
| 320 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); | ||
| 321 | cur_thread->SetMutexWaitAddressForDebugging(addr); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | // Cancel the timer wait. | ||
| 326 | kernel.TimeManager().UnscheduleTimeEvent(cur_thread); | ||
| 327 | |||
| 328 | // Remove from the condition variable. | ||
| 329 | { | ||
| 330 | KScopedSchedulerLock sl(kernel); | ||
| 331 | |||
| 332 | if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) { | ||
| 333 | owner->RemoveWaiter(cur_thread); | ||
| 334 | } | ||
| 335 | 321 | ||
| 336 | if (cur_thread->IsWaitingForConditionVariable()) { | 322 | // Begin waiting. |
| 337 | thread_tree.erase(thread_tree.iterator_to(*cur_thread)); | 323 | cur_thread->BeginWait(std::addressof(wait_queue)); |
| 338 | cur_thread->ClearConditionVariable(); | 324 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); |
| 339 | } | 325 | cur_thread->SetMutexWaitAddressForDebugging(addr); |
| 340 | } | 326 | } |
| 341 | 327 | ||
| 342 | // Get the result. | 328 | // Get the wait result. |
| 343 | KSynchronizationObject* dummy{}; | 329 | return cur_thread->GetWaitResult(); |
| 344 | return cur_thread->GetWaitResult(std::addressof(dummy)); | ||
| 345 | } | 330 | } |
| 346 | 331 | ||
| 347 | } // namespace Kernel | 332 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h index 861dbd420..5e4815d08 100644 --- a/src/core/hle/kernel/k_condition_variable.h +++ b/src/core/hle/kernel/k_condition_variable.h | |||
| @@ -34,7 +34,7 @@ public: | |||
| 34 | [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); | 34 | [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); |
| 35 | 35 | ||
| 36 | private: | 36 | private: |
| 37 | [[nodiscard]] KThread* SignalImpl(KThread* thread); | 37 | void SignalImpl(KThread* thread); |
| 38 | 38 | ||
| 39 | ThreadTree thread_tree; | 39 | ThreadTree thread_tree; |
| 40 | 40 | ||
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index e90fc0628..cf95f0852 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp | |||
| @@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() { | |||
| 13 | // Get the table and clear our record of it. | 13 | // Get the table and clear our record of it. |
| 14 | u16 saved_table_size = 0; | 14 | u16 saved_table_size = 0; |
| 15 | { | 15 | { |
| 16 | KScopedDisableDispatch dd(kernel); | ||
| 16 | KScopedSpinLock lk(m_lock); | 17 | KScopedSpinLock lk(m_lock); |
| 17 | 18 | ||
| 18 | std::swap(m_table_size, saved_table_size); | 19 | std::swap(m_table_size, saved_table_size); |
| @@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) { | |||
| 43 | // Find the object and free the entry. | 44 | // Find the object and free the entry. |
| 44 | KAutoObject* obj = nullptr; | 45 | KAutoObject* obj = nullptr; |
| 45 | { | 46 | { |
| 47 | KScopedDisableDispatch dd(kernel); | ||
| 46 | KScopedSpinLock lk(m_lock); | 48 | KScopedSpinLock lk(m_lock); |
| 47 | 49 | ||
| 48 | if (this->IsValidHandle(handle)) { | 50 | if (this->IsValidHandle(handle)) { |
| @@ -62,6 +64,7 @@ bool KHandleTable::Remove(Handle handle) { | |||
| 62 | } | 64 | } |
| 63 | 65 | ||
| 64 | ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { | 66 | ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { |
| 67 | KScopedDisableDispatch dd(kernel); | ||
| 65 | KScopedSpinLock lk(m_lock); | 68 | KScopedSpinLock lk(m_lock); |
| 66 | 69 | ||
| 67 | // Never exceed our capacity. | 70 | // Never exceed our capacity. |
| @@ -84,6 +87,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { | |||
| 84 | } | 87 | } |
| 85 | 88 | ||
| 86 | ResultCode KHandleTable::Reserve(Handle* out_handle) { | 89 | ResultCode KHandleTable::Reserve(Handle* out_handle) { |
| 90 | KScopedDisableDispatch dd(kernel); | ||
| 87 | KScopedSpinLock lk(m_lock); | 91 | KScopedSpinLock lk(m_lock); |
| 88 | 92 | ||
| 89 | // Never exceed our capacity. | 93 | // Never exceed our capacity. |
| @@ -94,6 +98,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) { | |||
| 94 | } | 98 | } |
| 95 | 99 | ||
| 96 | void KHandleTable::Unreserve(Handle handle) { | 100 | void KHandleTable::Unreserve(Handle handle) { |
| 101 | KScopedDisableDispatch dd(kernel); | ||
| 97 | KScopedSpinLock lk(m_lock); | 102 | KScopedSpinLock lk(m_lock); |
| 98 | 103 | ||
| 99 | // Unpack the handle. | 104 | // Unpack the handle. |
| @@ -112,6 +117,7 @@ void KHandleTable::Unreserve(Handle handle) { | |||
| 112 | } | 117 | } |
| 113 | 118 | ||
| 114 | void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { | 119 | void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { |
| 120 | KScopedDisableDispatch dd(kernel); | ||
| 115 | KScopedSpinLock lk(m_lock); | 121 | KScopedSpinLock lk(m_lock); |
| 116 | 122 | ||
| 117 | // Unpack the handle. | 123 | // Unpack the handle. |
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 95ec905ae..4b114ec2f 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h | |||
| @@ -68,6 +68,7 @@ public: | |||
| 68 | template <typename T = KAutoObject> | 68 | template <typename T = KAutoObject> |
| 69 | KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { | 69 | KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { |
| 70 | // Lock and look up in table. | 70 | // Lock and look up in table. |
| 71 | KScopedDisableDispatch dd(kernel); | ||
| 71 | KScopedSpinLock lk(m_lock); | 72 | KScopedSpinLock lk(m_lock); |
| 72 | 73 | ||
| 73 | if constexpr (std::is_same_v<T, KAutoObject>) { | 74 | if constexpr (std::is_same_v<T, KAutoObject>) { |
| @@ -122,6 +123,7 @@ public: | |||
| 122 | size_t num_opened; | 123 | size_t num_opened; |
| 123 | { | 124 | { |
| 124 | // Lock the table. | 125 | // Lock the table. |
| 126 | KScopedDisableDispatch dd(kernel); | ||
| 125 | KScopedSpinLock lk(m_lock); | 127 | KScopedSpinLock lk(m_lock); |
| 126 | for (num_opened = 0; num_opened < num_handles; num_opened++) { | 128 | for (num_opened = 0; num_opened < num_handles; num_opened++) { |
| 127 | // Get the current handle. | 129 | // Get the current handle. |
diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp new file mode 100644 index 000000000..a8001fffc --- /dev/null +++ b/src/core/hle/kernel/k_light_condition_variable.cpp | |||
| @@ -0,0 +1,80 @@ | |||
| 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/hle/kernel/k_light_condition_variable.h" | ||
| 6 | #include "core/hle/kernel/k_scheduler.h" | ||
| 7 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||
| 8 | #include "core/hle/kernel/k_thread_queue.h" | ||
| 9 | #include "core/hle/kernel/svc_results.h" | ||
| 10 | |||
| 11 | namespace Kernel { | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | |||
| 15 | class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue { | ||
| 16 | public: | ||
| 17 | ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl, | ||
| 18 | bool term) | ||
| 19 | : KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {} | ||
| 20 | |||
| 21 | void CancelWait(KThread* waiting_thread, ResultCode wait_result, | ||
| 22 | bool cancel_timer_task) override { | ||
| 23 | // Only process waits if we're allowed to. | ||
| 24 | if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) { | ||
| 25 | return; | ||
| 26 | } | ||
| 27 | |||
| 28 | // Remove the thread from the waiting thread from the light condition variable. | ||
| 29 | m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); | ||
| 30 | |||
| 31 | // Invoke the base cancel wait handler. | ||
| 32 | KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); | ||
| 33 | } | ||
| 34 | |||
| 35 | private: | ||
| 36 | KThread::WaiterList* m_wait_list; | ||
| 37 | bool m_allow_terminating_thread; | ||
| 38 | }; | ||
| 39 | |||
| 40 | } // namespace | ||
| 41 | |||
| 42 | void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) { | ||
| 43 | // Create thread queue. | ||
| 44 | KThread* owner = GetCurrentThreadPointer(kernel); | ||
| 45 | |||
| 46 | ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list), | ||
| 47 | allow_terminating_thread); | ||
| 48 | |||
| 49 | // Sleep the thread. | ||
| 50 | { | ||
| 51 | KScopedSchedulerLockAndSleep lk(kernel, owner, timeout); | ||
| 52 | |||
| 53 | if (!allow_terminating_thread && owner->IsTerminationRequested()) { | ||
| 54 | lk.CancelSleep(); | ||
| 55 | return; | ||
| 56 | } | ||
| 57 | |||
| 58 | lock->Unlock(); | ||
| 59 | |||
| 60 | // Add the thread to the queue. | ||
| 61 | wait_list.push_back(*owner); | ||
| 62 | |||
| 63 | // Begin waiting. | ||
| 64 | owner->BeginWait(std::addressof(wait_queue)); | ||
| 65 | } | ||
| 66 | |||
| 67 | // Re-acquire the lock. | ||
| 68 | lock->Lock(); | ||
| 69 | } | ||
| 70 | |||
| 71 | void KLightConditionVariable::Broadcast() { | ||
| 72 | KScopedSchedulerLock lk(kernel); | ||
| 73 | |||
| 74 | // Signal all threads. | ||
| 75 | for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) { | ||
| 76 | it->EndWait(ResultSuccess); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h index fb0ad783a..5d6d7f128 100644 --- a/src/core/hle/kernel/k_light_condition_variable.h +++ b/src/core/hle/kernel/k_light_condition_variable.h | |||
| @@ -2,72 +2,24 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | // This file references various implementation details from Atmosphere, an open-source firmware for | ||
| 6 | // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||
| 7 | |||
| 8 | #pragma once | 5 | #pragma once |
| 9 | 6 | ||
| 10 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 11 | #include "core/hle/kernel/k_scheduler.h" | 8 | #include "core/hle/kernel/k_thread.h" |
| 12 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||
| 13 | #include "core/hle/kernel/time_manager.h" | ||
| 14 | 9 | ||
| 15 | namespace Kernel { | 10 | namespace Kernel { |
| 11 | |||
| 16 | class KernelCore; | 12 | class KernelCore; |
| 13 | class KLightLock; | ||
| 17 | 14 | ||
| 18 | class KLightConditionVariable { | 15 | class KLightConditionVariable { |
| 19 | public: | 16 | public: |
| 20 | explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {} | 17 | explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {} |
| 21 | 18 | ||
| 22 | void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) { | 19 | void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true); |
| 23 | WaitImpl(lock, timeout, allow_terminating_thread); | 20 | void Broadcast(); |
| 24 | } | ||
| 25 | |||
| 26 | void Broadcast() { | ||
| 27 | KScopedSchedulerLock lk{kernel}; | ||
| 28 | |||
| 29 | // Signal all threads. | ||
| 30 | for (auto& thread : wait_list) { | ||
| 31 | thread.SetState(ThreadState::Runnable); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | 21 | ||
| 35 | private: | 22 | private: |
| 36 | void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) { | ||
| 37 | KThread* owner = GetCurrentThreadPointer(kernel); | ||
| 38 | |||
| 39 | // Sleep the thread. | ||
| 40 | { | ||
| 41 | KScopedSchedulerLockAndSleep lk{kernel, owner, timeout}; | ||
| 42 | |||
| 43 | if (!allow_terminating_thread && owner->IsTerminationRequested()) { | ||
| 44 | lk.CancelSleep(); | ||
| 45 | return; | ||
| 46 | } | ||
| 47 | |||
| 48 | lock->Unlock(); | ||
| 49 | |||
| 50 | // Set the thread as waiting. | ||
| 51 | GetCurrentThread(kernel).SetState(ThreadState::Waiting); | ||
| 52 | |||
| 53 | // Add the thread to the queue. | ||
| 54 | wait_list.push_back(GetCurrentThread(kernel)); | ||
| 55 | } | ||
| 56 | |||
| 57 | // Remove the thread from the wait list. | ||
| 58 | { | ||
| 59 | KScopedSchedulerLock sl{kernel}; | ||
| 60 | |||
| 61 | wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel))); | ||
| 62 | } | ||
| 63 | |||
| 64 | // Cancel the task that the sleep setup. | ||
| 65 | kernel.TimeManager().UnscheduleTimeEvent(owner); | ||
| 66 | |||
| 67 | // Re-acquire the lock. | ||
| 68 | lock->Lock(); | ||
| 69 | } | ||
| 70 | |||
| 71 | KernelCore& kernel; | 23 | KernelCore& kernel; |
| 72 | KThread::WaiterList wait_list{}; | 24 | KThread::WaiterList wait_list{}; |
| 73 | }; | 25 | }; |
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp index 0896e705f..4620342eb 100644 --- a/src/core/hle/kernel/k_light_lock.cpp +++ b/src/core/hle/kernel/k_light_lock.cpp | |||
| @@ -5,44 +5,59 @@ | |||
| 5 | #include "core/hle/kernel/k_light_lock.h" | 5 | #include "core/hle/kernel/k_light_lock.h" |
| 6 | #include "core/hle/kernel/k_scheduler.h" | 6 | #include "core/hle/kernel/k_scheduler.h" |
| 7 | #include "core/hle/kernel/k_thread.h" | 7 | #include "core/hle/kernel/k_thread.h" |
| 8 | #include "core/hle/kernel/k_thread_queue.h" | ||
| 8 | #include "core/hle/kernel/kernel.h" | 9 | #include "core/hle/kernel/kernel.h" |
| 9 | 10 | ||
| 10 | namespace Kernel { | 11 | namespace Kernel { |
| 11 | 12 | ||
| 13 | namespace { | ||
| 14 | |||
| 15 | class ThreadQueueImplForKLightLock final : public KThreadQueue { | ||
| 16 | public: | ||
| 17 | explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {} | ||
| 18 | |||
| 19 | void CancelWait(KThread* waiting_thread, ResultCode wait_result, | ||
| 20 | bool cancel_timer_task) override { | ||
| 21 | // Remove the thread as a waiter from its owner. | ||
| 22 | if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) { | ||
| 23 | owner->RemoveWaiter(waiting_thread); | ||
| 24 | } | ||
| 25 | |||
| 26 | // Invoke the base cancel wait handler. | ||
| 27 | KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | } // namespace | ||
| 32 | |||
| 12 | void KLightLock::Lock() { | 33 | void KLightLock::Lock() { |
| 13 | const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); | 34 | const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); |
| 14 | const uintptr_t cur_thread_tag = (cur_thread | 1); | ||
| 15 | 35 | ||
| 16 | while (true) { | 36 | while (true) { |
| 17 | uintptr_t old_tag = tag.load(std::memory_order_relaxed); | 37 | uintptr_t old_tag = tag.load(std::memory_order_relaxed); |
| 18 | 38 | ||
| 19 | while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1, | 39 | while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1), |
| 20 | std::memory_order_acquire)) { | 40 | std::memory_order_acquire)) { |
| 21 | if ((old_tag | 1) == cur_thread_tag) { | ||
| 22 | return; | ||
| 23 | } | ||
| 24 | } | 41 | } |
| 25 | 42 | ||
| 26 | if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) { | 43 | if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) { |
| 27 | break; | 44 | break; |
| 28 | } | 45 | } |
| 29 | |||
| 30 | LockSlowPath(old_tag | 1, cur_thread); | ||
| 31 | } | 46 | } |
| 32 | } | 47 | } |
| 33 | 48 | ||
| 34 | void KLightLock::Unlock() { | 49 | void KLightLock::Unlock() { |
| 35 | const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); | 50 | const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); |
| 51 | |||
| 36 | uintptr_t expected = cur_thread; | 52 | uintptr_t expected = cur_thread; |
| 37 | do { | 53 | if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) { |
| 38 | if (expected != cur_thread) { | 54 | this->UnlockSlowPath(cur_thread); |
| 39 | return UnlockSlowPath(cur_thread); | 55 | } |
| 40 | } | ||
| 41 | } while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release)); | ||
| 42 | } | 56 | } |
| 43 | 57 | ||
| 44 | void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { | 58 | bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { |
| 45 | KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread); | 59 | KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread); |
| 60 | ThreadQueueImplForKLightLock wait_queue(kernel); | ||
| 46 | 61 | ||
| 47 | // Pend the current thread waiting on the owner thread. | 62 | // Pend the current thread waiting on the owner thread. |
| 48 | { | 63 | { |
| @@ -50,7 +65,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { | |||
| 50 | 65 | ||
| 51 | // Ensure we actually have locking to do. | 66 | // Ensure we actually have locking to do. |
| 52 | if (tag.load(std::memory_order_relaxed) != _owner) { | 67 | if (tag.load(std::memory_order_relaxed) != _owner) { |
| 53 | return; | 68 | return false; |
| 54 | } | 69 | } |
| 55 | 70 | ||
| 56 | // Add the current thread as a waiter on the owner. | 71 | // Add the current thread as a waiter on the owner. |
| @@ -58,22 +73,15 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { | |||
| 58 | cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); | 73 | cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); |
| 59 | owner_thread->AddWaiter(cur_thread); | 74 | owner_thread->AddWaiter(cur_thread); |
| 60 | 75 | ||
| 61 | // Set thread states. | 76 | // Begin waiting to hold the lock. |
| 62 | cur_thread->SetState(ThreadState::Waiting); | 77 | cur_thread->BeginWait(std::addressof(wait_queue)); |
| 63 | 78 | ||
| 64 | if (owner_thread->IsSuspended()) { | 79 | if (owner_thread->IsSuspended()) { |
| 65 | owner_thread->ContinueIfHasKernelWaiters(); | 80 | owner_thread->ContinueIfHasKernelWaiters(); |
| 66 | } | 81 | } |
| 67 | } | 82 | } |
| 68 | 83 | ||
| 69 | // We're no longer waiting on the lock owner. | 84 | return true; |
| 70 | { | ||
| 71 | KScopedSchedulerLock sl{kernel}; | ||
| 72 | |||
| 73 | if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) { | ||
| 74 | owner_thread->RemoveWaiter(cur_thread); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | 85 | } |
| 78 | 86 | ||
| 79 | void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { | 87 | void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { |
| @@ -81,22 +89,20 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { | |||
| 81 | 89 | ||
| 82 | // Unlock. | 90 | // Unlock. |
| 83 | { | 91 | { |
| 84 | KScopedSchedulerLock sl{kernel}; | 92 | KScopedSchedulerLock sl(kernel); |
| 85 | 93 | ||
| 86 | // Get the next owner. | 94 | // Get the next owner. |
| 87 | s32 num_waiters = 0; | 95 | s32 num_waiters; |
| 88 | KThread* next_owner = owner_thread->RemoveWaiterByKey( | 96 | KThread* next_owner = owner_thread->RemoveWaiterByKey( |
| 89 | std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag))); | 97 | std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag))); |
| 90 | 98 | ||
| 91 | // Pass the lock to the next owner. | 99 | // Pass the lock to the next owner. |
| 92 | uintptr_t next_tag = 0; | 100 | uintptr_t next_tag = 0; |
| 93 | if (next_owner != nullptr) { | 101 | if (next_owner != nullptr) { |
| 94 | next_tag = reinterpret_cast<uintptr_t>(next_owner); | 102 | next_tag = |
| 95 | if (num_waiters > 1) { | 103 | reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1); |
| 96 | next_tag |= 0x1; | ||
| 97 | } | ||
| 98 | 104 | ||
| 99 | next_owner->SetState(ThreadState::Runnable); | 105 | next_owner->EndWait(ResultSuccess); |
| 100 | 106 | ||
| 101 | if (next_owner->IsSuspended()) { | 107 | if (next_owner->IsSuspended()) { |
| 102 | next_owner->ContinueIfHasKernelWaiters(); | 108 | next_owner->ContinueIfHasKernelWaiters(); |
| @@ -110,7 +116,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { | |||
| 110 | } | 116 | } |
| 111 | 117 | ||
| 112 | // Write the new tag value. | 118 | // Write the new tag value. |
| 113 | tag.store(next_tag); | 119 | tag.store(next_tag, std::memory_order_release); |
| 114 | } | 120 | } |
| 115 | } | 121 | } |
| 116 | 122 | ||
diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h index ad853661d..4163b8a85 100644 --- a/src/core/hle/kernel/k_light_lock.h +++ b/src/core/hle/kernel/k_light_lock.h | |||
| @@ -20,7 +20,7 @@ public: | |||
| 20 | 20 | ||
| 21 | void Unlock(); | 21 | void Unlock(); |
| 22 | 22 | ||
| 23 | void LockSlowPath(uintptr_t owner, uintptr_t cur_thread); | 23 | bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread); |
| 24 | 24 | ||
| 25 | void UnlockSlowPath(uintptr_t cur_thread); | 25 | void UnlockSlowPath(uintptr_t cur_thread); |
| 26 | 26 | ||
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index a7fdb5fb8..fd491146f 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h | |||
| @@ -131,6 +131,26 @@ enum class KMemoryPermission : u8 { | |||
| 131 | 131 | ||
| 132 | UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | | 132 | UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | |
| 133 | Svc::MemoryPermission::Execute), | 133 | Svc::MemoryPermission::Execute), |
| 134 | |||
| 135 | KernelShift = 3, | ||
| 136 | |||
| 137 | KernelRead = Read << KernelShift, | ||
| 138 | KernelWrite = Write << KernelShift, | ||
| 139 | KernelExecute = Execute << KernelShift, | ||
| 140 | |||
| 141 | NotMapped = (1 << (2 * KernelShift)), | ||
| 142 | |||
| 143 | KernelReadWrite = KernelRead | KernelWrite, | ||
| 144 | KernelReadExecute = KernelRead | KernelExecute, | ||
| 145 | |||
| 146 | UserRead = Read | KernelRead, | ||
| 147 | UserWrite = Write | KernelWrite, | ||
| 148 | UserExecute = Execute, | ||
| 149 | |||
| 150 | UserReadWrite = UserRead | UserWrite, | ||
| 151 | UserReadExecute = UserRead | UserExecute, | ||
| 152 | |||
| 153 | IpcLockChangeMask = NotMapped | UserReadWrite | ||
| 134 | }; | 154 | }; |
| 135 | DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); | 155 | DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); |
| 136 | 156 | ||
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h index 3362fb236..0e2ae582a 100644 --- a/src/core/hle/kernel/k_page_linked_list.h +++ b/src/core/hle/kernel/k_page_linked_list.h | |||
| @@ -27,6 +27,10 @@ public: | |||
| 27 | return num_pages; | 27 | return num_pages; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | constexpr std::size_t GetSize() const { | ||
| 31 | return GetNumPages() * PageSize; | ||
| 32 | } | ||
| 33 | |||
| 30 | private: | 34 | private: |
| 31 | u64 addr{}; | 35 | u64 addr{}; |
| 32 | std::size_t num_pages{}; | 36 | std::size_t num_pages{}; |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 526b87241..99982e5a3 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -368,6 +368,33 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st | |||
| 368 | return ResultSuccess; | 368 | return ResultSuccess; |
| 369 | } | 369 | } |
| 370 | 370 | ||
| 371 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, | ||
| 372 | KPageTable& src_page_table, VAddr src_addr) { | ||
| 373 | std::lock_guard lock{page_table_lock}; | ||
| 374 | |||
| 375 | const std::size_t num_pages{size / PageSize}; | ||
| 376 | |||
| 377 | // Check that the memory is mapped in the destination process. | ||
| 378 | size_t num_allocator_blocks; | ||
| 379 | R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All, | ||
| 380 | KMemoryState::SharedCode, KMemoryPermission::UserReadWrite, | ||
| 381 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 382 | KMemoryAttribute::None)); | ||
| 383 | |||
| 384 | // Check that the memory is mapped in the source process. | ||
| 385 | R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess, | ||
| 386 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, | ||
| 387 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 388 | KMemoryAttribute::None)); | ||
| 389 | |||
| 390 | CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); | ||
| 391 | |||
| 392 | // Apply the memory block update. | ||
| 393 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, | ||
| 394 | KMemoryAttribute::None); | ||
| 395 | |||
| 396 | return ResultSuccess; | ||
| 397 | } | ||
| 371 | void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) { | 398 | void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) { |
| 372 | auto node{page_linked_list.Nodes().begin()}; | 399 | auto node{page_linked_list.Nodes().begin()}; |
| 373 | PAddr map_addr{node->GetAddress()}; | 400 | PAddr map_addr{node->GetAddress()}; |
| @@ -685,8 +712,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, | |||
| 685 | return ResultSuccess; | 712 | return ResultSuccess; |
| 686 | } | 713 | } |
| 687 | 714 | ||
| 688 | ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size, | 715 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, |
| 689 | KMemoryPermission perm) { | 716 | KMemoryPermission perm) { |
| 690 | 717 | ||
| 691 | std::lock_guard lock{page_table_lock}; | 718 | std::lock_guard lock{page_table_lock}; |
| 692 | 719 | ||
| @@ -942,6 +969,60 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) | |||
| 942 | return ResultSuccess; | 969 | return ResultSuccess; |
| 943 | } | 970 | } |
| 944 | 971 | ||
| 972 | ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { | ||
| 973 | std::lock_guard lock{page_table_lock}; | ||
| 974 | |||
| 975 | KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; | ||
| 976 | |||
| 977 | KMemoryPermission old_perm{}; | ||
| 978 | |||
| 979 | if (const ResultCode result{CheckMemoryState( | ||
| 980 | nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | ||
| 981 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask, | ||
| 982 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; | ||
| 983 | result.IsError()) { | ||
| 984 | return result; | ||
| 985 | } | ||
| 986 | |||
| 987 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 988 | |||
| 989 | block_manager->UpdateLock( | ||
| 990 | addr, size / PageSize, | ||
| 991 | [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { | ||
| 992 | block->ShareToDevice(permission); | ||
| 993 | }, | ||
| 994 | new_perm); | ||
| 995 | |||
| 996 | return ResultSuccess; | ||
| 997 | } | ||
| 998 | |||
| 999 | ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { | ||
| 1000 | std::lock_guard lock{page_table_lock}; | ||
| 1001 | |||
| 1002 | KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; | ||
| 1003 | |||
| 1004 | KMemoryPermission old_perm{}; | ||
| 1005 | |||
| 1006 | if (const ResultCode result{CheckMemoryState( | ||
| 1007 | nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | ||
| 1008 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, | ||
| 1009 | KMemoryAttribute::All, KMemoryAttribute::Locked)}; | ||
| 1010 | result.IsError()) { | ||
| 1011 | return result; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 1015 | |||
| 1016 | block_manager->UpdateLock( | ||
| 1017 | addr, size / PageSize, | ||
| 1018 | [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { | ||
| 1019 | block->UnshareToDevice(permission); | ||
| 1020 | }, | ||
| 1021 | new_perm); | ||
| 1022 | |||
| 1023 | return ResultSuccess; | ||
| 1024 | } | ||
| 1025 | |||
| 945 | ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { | 1026 | ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { |
| 946 | block_manager = std::make_unique<KMemoryBlockManager>(start, end); | 1027 | block_manager = std::make_unique<KMemoryBlockManager>(start, end); |
| 947 | 1028 | ||
| @@ -1231,4 +1312,42 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi | |||
| 1231 | return ResultSuccess; | 1312 | return ResultSuccess; |
| 1232 | } | 1313 | } |
| 1233 | 1314 | ||
| 1315 | ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size, | ||
| 1316 | KMemoryState state_mask, KMemoryState state, | ||
| 1317 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 1318 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const { | ||
| 1319 | // Get information about the first block. | ||
| 1320 | const VAddr last_addr = addr + size - 1; | ||
| 1321 | KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)}; | ||
| 1322 | KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1323 | |||
| 1324 | // If the start address isn't aligned, we need a block. | ||
| 1325 | const size_t blocks_for_start_align = | ||
| 1326 | (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; | ||
| 1327 | |||
| 1328 | while (true) { | ||
| 1329 | // Validate against the provided masks. | ||
| 1330 | R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); | ||
| 1331 | |||
| 1332 | // Break once we're done. | ||
| 1333 | if (last_addr <= info.GetLastAddress()) { | ||
| 1334 | break; | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | // Advance our iterator. | ||
| 1338 | it++; | ||
| 1339 | info = it->GetMemoryInfo(); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | // If the end address isn't aligned, we need a block. | ||
| 1343 | const size_t blocks_for_end_align = | ||
| 1344 | (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0; | ||
| 1345 | |||
| 1346 | if (out_blocks_needed != nullptr) { | ||
| 1347 | *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | return ResultSuccess; | ||
| 1351 | } | ||
| 1352 | |||
| 1234 | } // namespace Kernel | 1353 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 770c4841c..d784aa67e 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -33,6 +33,8 @@ public: | |||
| 33 | KMemoryPermission perm); | 33 | KMemoryPermission perm); |
| 34 | ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 34 | ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); |
| 35 | ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 35 | ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); |
| 36 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, | ||
| 37 | VAddr src_addr); | ||
| 36 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); | 38 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); |
| 37 | ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); | 39 | ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); |
| 38 | ResultCode UnmapMemory(VAddr addr, std::size_t size); | 40 | ResultCode UnmapMemory(VAddr addr, std::size_t size); |
| @@ -41,7 +43,7 @@ public: | |||
| 41 | ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, | 43 | ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, |
| 42 | KMemoryPermission perm); | 44 | KMemoryPermission perm); |
| 43 | ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); | 45 | ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); |
| 44 | ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm); | 46 | ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm); |
| 45 | KMemoryInfo QueryInfo(VAddr addr); | 47 | KMemoryInfo QueryInfo(VAddr addr); |
| 46 | ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); | 48 | ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); |
| 47 | ResultCode ResetTransferMemory(VAddr addr, std::size_t size); | 49 | ResultCode ResetTransferMemory(VAddr addr, std::size_t size); |
| @@ -55,6 +57,8 @@ public: | |||
| 55 | KMemoryPermission perm, PAddr map_addr = 0); | 57 | KMemoryPermission perm, PAddr map_addr = 0); |
| 56 | ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); | 58 | ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); |
| 57 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); | 59 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); |
| 60 | ResultCode LockForCodeMemory(VAddr addr, std::size_t size); | ||
| 61 | ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); | ||
| 58 | 62 | ||
| 59 | Common::PageTable& PageTableImpl() { | 63 | Common::PageTable& PageTableImpl() { |
| 60 | return page_table_impl; | 64 | return page_table_impl; |
| @@ -115,6 +119,10 @@ private: | |||
| 115 | return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, | 119 | return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, |
| 116 | perm, attr_mask, attr, ignore_attr); | 120 | perm, attr_mask, attr, ignore_attr); |
| 117 | } | 121 | } |
| 122 | ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size, | ||
| 123 | KMemoryState state_mask, KMemoryState state, | ||
| 124 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 125 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const; | ||
| 118 | 126 | ||
| 119 | std::recursive_mutex page_table_lock; | 127 | std::recursive_mutex page_table_lock; |
| 120 | std::unique_ptr<KMemoryBlockManager> block_manager; | 128 | std::unique_ptr<KMemoryBlockManager> block_manager; |
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 76fd8c285..90dda40dc 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -60,6 +60,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority | |||
| 60 | thread->GetContext64().cpu_registers[0] = 0; | 60 | thread->GetContext64().cpu_registers[0] = 0; |
| 61 | thread->GetContext32().cpu_registers[1] = thread_handle; | 61 | thread->GetContext32().cpu_registers[1] = thread_handle; |
| 62 | thread->GetContext64().cpu_registers[1] = thread_handle; | 62 | thread->GetContext64().cpu_registers[1] = thread_handle; |
| 63 | thread->DisableDispatch(); | ||
| 63 | 64 | ||
| 64 | auto& kernel = system.Kernel(); | 65 | auto& kernel = system.Kernel(); |
| 65 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires | 66 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires |
| @@ -227,12 +228,15 @@ void KProcess::PinCurrentThread() { | |||
| 227 | const s32 core_id = GetCurrentCoreId(kernel); | 228 | const s32 core_id = GetCurrentCoreId(kernel); |
| 228 | KThread* cur_thread = GetCurrentThreadPointer(kernel); | 229 | KThread* cur_thread = GetCurrentThreadPointer(kernel); |
| 229 | 230 | ||
| 230 | // Pin it. | 231 | // If the thread isn't terminated, pin it. |
| 231 | PinThread(core_id, cur_thread); | 232 | if (!cur_thread->IsTerminationRequested()) { |
| 232 | cur_thread->Pin(); | 233 | // Pin it. |
| 234 | PinThread(core_id, cur_thread); | ||
| 235 | cur_thread->Pin(); | ||
| 233 | 236 | ||
| 234 | // An update is needed. | 237 | // An update is needed. |
| 235 | KScheduler::SetSchedulerUpdateNeeded(kernel); | 238 | KScheduler::SetSchedulerUpdateNeeded(kernel); |
| 239 | } | ||
| 236 | } | 240 | } |
| 237 | 241 | ||
| 238 | void KProcess::UnpinCurrentThread() { | 242 | void KProcess::UnpinCurrentThread() { |
| @@ -250,6 +254,20 @@ void KProcess::UnpinCurrentThread() { | |||
| 250 | KScheduler::SetSchedulerUpdateNeeded(kernel); | 254 | KScheduler::SetSchedulerUpdateNeeded(kernel); |
| 251 | } | 255 | } |
| 252 | 256 | ||
| 257 | void KProcess::UnpinThread(KThread* thread) { | ||
| 258 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | ||
| 259 | |||
| 260 | // Get the thread's core id. | ||
| 261 | const auto core_id = thread->GetActiveCore(); | ||
| 262 | |||
| 263 | // Unpin it. | ||
| 264 | UnpinThread(core_id, thread); | ||
| 265 | thread->Unpin(); | ||
| 266 | |||
| 267 | // An update is needed. | ||
| 268 | KScheduler::SetSchedulerUpdateNeeded(kernel); | ||
| 269 | } | ||
| 270 | |||
| 253 | ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, | 271 | ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, |
| 254 | [[maybe_unused]] size_t size) { | 272 | [[maybe_unused]] size_t size) { |
| 255 | // Lock ourselves, to prevent concurrent access. | 273 | // Lock ourselves, to prevent concurrent access. |
| @@ -528,7 +546,7 @@ void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { | |||
| 528 | std::lock_guard lock{HLE::g_hle_lock}; | 546 | std::lock_guard lock{HLE::g_hle_lock}; |
| 529 | const auto ReprotectSegment = [&](const CodeSet::Segment& segment, | 547 | const auto ReprotectSegment = [&](const CodeSet::Segment& segment, |
| 530 | KMemoryPermission permission) { | 548 | KMemoryPermission permission) { |
| 531 | page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); | 549 | page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); |
| 532 | }; | 550 | }; |
| 533 | 551 | ||
| 534 | kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), | 552 | kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), |
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 8a8c1fcbb..cb93c7e24 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -347,6 +347,7 @@ public: | |||
| 347 | 347 | ||
| 348 | void PinCurrentThread(); | 348 | void PinCurrentThread(); |
| 349 | void UnpinCurrentThread(); | 349 | void UnpinCurrentThread(); |
| 350 | void UnpinThread(KThread* thread); | ||
| 350 | 351 | ||
| 351 | KLightLock& GetStateLock() { | 352 | KLightLock& GetStateLock() { |
| 352 | return state_lock; | 353 | return state_lock; |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 6a7d80d03..277201de4 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -240,8 +240,8 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3 | |||
| 240 | 240 | ||
| 241 | // If the thread is runnable, we want to change its priority in the queue. | 241 | // If the thread is runnable, we want to change its priority in the queue. |
| 242 | if (thread->GetRawState() == ThreadState::Runnable) { | 242 | if (thread->GetRawState() == ThreadState::Runnable) { |
| 243 | GetPriorityQueue(kernel).ChangePriority( | 243 | GetPriorityQueue(kernel).ChangePriority(old_priority, |
| 244 | old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread); | 244 | thread == kernel.GetCurrentEmuThread(), thread); |
| 245 | IncrementScheduledCount(thread); | 245 | IncrementScheduledCount(thread); |
| 246 | SetSchedulerUpdateNeeded(kernel); | 246 | SetSchedulerUpdateNeeded(kernel); |
| 247 | } | 247 | } |
| @@ -360,7 +360,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) { | |||
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | bool KScheduler::CanSchedule(KernelCore& kernel) { | 362 | bool KScheduler::CanSchedule(KernelCore& kernel) { |
| 363 | return kernel.CurrentScheduler()->GetCurrentThread()->GetDisableDispatchCount() <= 1; | 363 | return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1; |
| 364 | } | 364 | } |
| 365 | 365 | ||
| 366 | bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) { | 366 | bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) { |
| @@ -376,20 +376,30 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) { | |||
| 376 | } | 376 | } |
| 377 | 377 | ||
| 378 | void KScheduler::DisableScheduling(KernelCore& kernel) { | 378 | void KScheduler::DisableScheduling(KernelCore& kernel) { |
| 379 | if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { | 379 | // If we are shutting down the kernel, none of this is relevant anymore. |
| 380 | ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0); | 380 | if (kernel.IsShuttingDown()) { |
| 381 | scheduler->GetCurrentThread()->DisableDispatch(); | 381 | return; |
| 382 | } | 382 | } |
| 383 | |||
| 384 | ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0); | ||
| 385 | GetCurrentThreadPointer(kernel)->DisableDispatch(); | ||
| 383 | } | 386 | } |
| 384 | 387 | ||
| 385 | void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { | 388 | void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { |
| 386 | if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { | 389 | // If we are shutting down the kernel, none of this is relevant anymore. |
| 387 | ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); | 390 | if (kernel.IsShuttingDown()) { |
| 388 | if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { | 391 | return; |
| 389 | scheduler->GetCurrentThread()->EnableDispatch(); | 392 | } |
| 390 | } | 393 | |
| 394 | auto* current_thread = GetCurrentThreadPointer(kernel); | ||
| 395 | |||
| 396 | ASSERT(current_thread->GetDisableDispatchCount() >= 1); | ||
| 397 | |||
| 398 | if (current_thread->GetDisableDispatchCount() > 1) { | ||
| 399 | current_thread->EnableDispatch(); | ||
| 400 | } else { | ||
| 401 | RescheduleCores(kernel, cores_needing_scheduling); | ||
| 391 | } | 402 | } |
| 392 | RescheduleCores(kernel, cores_needing_scheduling); | ||
| 393 | } | 403 | } |
| 394 | 404 | ||
| 395 | u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { | 405 | u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { |
| @@ -617,13 +627,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c | |||
| 617 | state.highest_priority_thread = nullptr; | 627 | state.highest_priority_thread = nullptr; |
| 618 | } | 628 | } |
| 619 | 629 | ||
| 620 | KScheduler::~KScheduler() { | 630 | void KScheduler::Finalize() { |
| 621 | if (idle_thread) { | 631 | if (idle_thread) { |
| 622 | idle_thread->Close(); | 632 | idle_thread->Close(); |
| 623 | idle_thread = nullptr; | 633 | idle_thread = nullptr; |
| 624 | } | 634 | } |
| 625 | } | 635 | } |
| 626 | 636 | ||
| 637 | KScheduler::~KScheduler() { | ||
| 638 | ASSERT(!idle_thread); | ||
| 639 | } | ||
| 640 | |||
| 627 | KThread* KScheduler::GetCurrentThread() const { | 641 | KThread* KScheduler::GetCurrentThread() const { |
| 628 | if (auto result = current_thread.load(); result) { | 642 | if (auto result = current_thread.load(); result) { |
| 629 | return result; | 643 | return result; |
| @@ -642,10 +656,12 @@ void KScheduler::RescheduleCurrentCore() { | |||
| 642 | if (phys_core.IsInterrupted()) { | 656 | if (phys_core.IsInterrupted()) { |
| 643 | phys_core.ClearInterrupt(); | 657 | phys_core.ClearInterrupt(); |
| 644 | } | 658 | } |
| 659 | |||
| 645 | guard.Lock(); | 660 | guard.Lock(); |
| 646 | if (state.needs_scheduling.load()) { | 661 | if (state.needs_scheduling.load()) { |
| 647 | Schedule(); | 662 | Schedule(); |
| 648 | } else { | 663 | } else { |
| 664 | GetCurrentThread()->EnableDispatch(); | ||
| 649 | guard.Unlock(); | 665 | guard.Unlock(); |
| 650 | } | 666 | } |
| 651 | } | 667 | } |
| @@ -655,26 +671,33 @@ void KScheduler::OnThreadStart() { | |||
| 655 | } | 671 | } |
| 656 | 672 | ||
| 657 | void KScheduler::Unload(KThread* thread) { | 673 | void KScheduler::Unload(KThread* thread) { |
| 674 | ASSERT(thread); | ||
| 675 | |||
| 658 | LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); | 676 | LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); |
| 659 | 677 | ||
| 660 | if (thread) { | 678 | if (thread->IsCallingSvc()) { |
| 661 | if (thread->IsCallingSvc()) { | 679 | thread->ClearIsCallingSvc(); |
| 662 | thread->ClearIsCallingSvc(); | 680 | } |
| 663 | } | 681 | |
| 664 | if (!thread->IsTerminationRequested()) { | 682 | auto& physical_core = system.Kernel().PhysicalCore(core_id); |
| 665 | prev_thread = thread; | 683 | if (!physical_core.IsInitialized()) { |
| 666 | 684 | return; | |
| 667 | Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); | 685 | } |
| 668 | cpu_core.SaveContext(thread->GetContext32()); | 686 | |
| 669 | cpu_core.SaveContext(thread->GetContext64()); | 687 | Core::ARM_Interface& cpu_core = physical_core.ArmInterface(); |
| 670 | // Save the TPIDR_EL0 system register in case it was modified. | 688 | cpu_core.SaveContext(thread->GetContext32()); |
| 671 | thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); | 689 | cpu_core.SaveContext(thread->GetContext64()); |
| 672 | cpu_core.ClearExclusiveState(); | 690 | // Save the TPIDR_EL0 system register in case it was modified. |
| 673 | } else { | 691 | thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); |
| 674 | prev_thread = nullptr; | 692 | cpu_core.ClearExclusiveState(); |
| 675 | } | 693 | |
| 676 | thread->context_guard.Unlock(); | 694 | if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) { |
| 695 | prev_thread = thread; | ||
| 696 | } else { | ||
| 697 | prev_thread = nullptr; | ||
| 677 | } | 698 | } |
| 699 | |||
| 700 | thread->context_guard.Unlock(); | ||
| 678 | } | 701 | } |
| 679 | 702 | ||
| 680 | void KScheduler::Reload(KThread* thread) { | 703 | void KScheduler::Reload(KThread* thread) { |
| @@ -683,11 +706,6 @@ void KScheduler::Reload(KThread* thread) { | |||
| 683 | if (thread) { | 706 | if (thread) { |
| 684 | ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); | 707 | ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); |
| 685 | 708 | ||
| 686 | auto* const thread_owner_process = thread->GetOwnerProcess(); | ||
| 687 | if (thread_owner_process != nullptr) { | ||
| 688 | system.Kernel().MakeCurrentProcess(thread_owner_process); | ||
| 689 | } | ||
| 690 | |||
| 691 | Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); | 709 | Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); |
| 692 | cpu_core.LoadContext(thread->GetContext32()); | 710 | cpu_core.LoadContext(thread->GetContext32()); |
| 693 | cpu_core.LoadContext(thread->GetContext64()); | 711 | cpu_core.LoadContext(thread->GetContext64()); |
| @@ -705,7 +723,7 @@ void KScheduler::SwitchContextStep2() { | |||
| 705 | } | 723 | } |
| 706 | 724 | ||
| 707 | void KScheduler::ScheduleImpl() { | 725 | void KScheduler::ScheduleImpl() { |
| 708 | KThread* previous_thread = current_thread.load(); | 726 | KThread* previous_thread = GetCurrentThread(); |
| 709 | KThread* next_thread = state.highest_priority_thread; | 727 | KThread* next_thread = state.highest_priority_thread; |
| 710 | 728 | ||
| 711 | state.needs_scheduling = false; | 729 | state.needs_scheduling = false; |
| @@ -717,10 +735,15 @@ void KScheduler::ScheduleImpl() { | |||
| 717 | 735 | ||
| 718 | // If we're not actually switching thread, there's nothing to do. | 736 | // If we're not actually switching thread, there's nothing to do. |
| 719 | if (next_thread == current_thread.load()) { | 737 | if (next_thread == current_thread.load()) { |
| 738 | previous_thread->EnableDispatch(); | ||
| 720 | guard.Unlock(); | 739 | guard.Unlock(); |
| 721 | return; | 740 | return; |
| 722 | } | 741 | } |
| 723 | 742 | ||
| 743 | if (next_thread->GetCurrentCore() != core_id) { | ||
| 744 | next_thread->SetCurrentCore(core_id); | ||
| 745 | } | ||
| 746 | |||
| 724 | current_thread.store(next_thread); | 747 | current_thread.store(next_thread); |
| 725 | 748 | ||
| 726 | KProcess* const previous_process = system.Kernel().CurrentProcess(); | 749 | KProcess* const previous_process = system.Kernel().CurrentProcess(); |
| @@ -731,11 +754,7 @@ void KScheduler::ScheduleImpl() { | |||
| 731 | Unload(previous_thread); | 754 | Unload(previous_thread); |
| 732 | 755 | ||
| 733 | std::shared_ptr<Common::Fiber>* old_context; | 756 | std::shared_ptr<Common::Fiber>* old_context; |
| 734 | if (previous_thread != nullptr) { | 757 | old_context = &previous_thread->GetHostContext(); |
| 735 | old_context = &previous_thread->GetHostContext(); | ||
| 736 | } else { | ||
| 737 | old_context = &idle_thread->GetHostContext(); | ||
| 738 | } | ||
| 739 | guard.Unlock(); | 758 | guard.Unlock(); |
| 740 | 759 | ||
| 741 | Common::Fiber::YieldTo(*old_context, *switch_fiber); | 760 | Common::Fiber::YieldTo(*old_context, *switch_fiber); |
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 7df288438..82fcd99e7 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h | |||
| @@ -33,6 +33,8 @@ public: | |||
| 33 | explicit KScheduler(Core::System& system_, s32 core_id_); | 33 | explicit KScheduler(Core::System& system_, s32 core_id_); |
| 34 | ~KScheduler(); | 34 | ~KScheduler(); |
| 35 | 35 | ||
| 36 | void Finalize(); | ||
| 37 | |||
| 36 | /// Reschedules to the next available thread (call after current thread is suspended) | 38 | /// Reschedules to the next available thread (call after current thread is suspended) |
| 37 | void RescheduleCurrentCore(); | 39 | void RescheduleCurrentCore(); |
| 38 | 40 | ||
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index c571f2992..93c47f1b1 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h | |||
| @@ -23,6 +23,11 @@ public: | |||
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | void Lock() { | 25 | void Lock() { |
| 26 | // If we are shutting down the kernel, none of this is relevant anymore. | ||
| 27 | if (kernel.IsShuttingDown()) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | |||
| 26 | if (IsLockedByCurrentThread()) { | 31 | if (IsLockedByCurrentThread()) { |
| 27 | // If we already own the lock, we can just increment the count. | 32 | // If we already own the lock, we can just increment the count. |
| 28 | ASSERT(lock_count > 0); | 33 | ASSERT(lock_count > 0); |
| @@ -43,6 +48,11 @@ public: | |||
| 43 | } | 48 | } |
| 44 | 49 | ||
| 45 | void Unlock() { | 50 | void Unlock() { |
| 51 | // If we are shutting down the kernel, none of this is relevant anymore. | ||
| 52 | if (kernel.IsShuttingDown()) { | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | |||
| 46 | ASSERT(IsLockedByCurrentThread()); | 56 | ASSERT(IsLockedByCurrentThread()); |
| 47 | ASSERT(lock_count > 0); | 57 | ASSERT(lock_count > 0); |
| 48 | 58 | ||
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index 61dc2858f..2995c492d 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #pragma once | 8 | #pragma once |
| 9 | 9 | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/hle/kernel/global_scheduler_context.h" | ||
| 11 | #include "core/hle/kernel/k_thread.h" | 12 | #include "core/hle/kernel/k_thread.h" |
| 12 | #include "core/hle/kernel/kernel.h" | 13 | #include "core/hle/kernel/kernel.h" |
| 13 | #include "core/hle/kernel/time_manager.h" | 14 | #include "core/hle/kernel/time_manager.h" |
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 2bd53ccbd..d4e4a6b06 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp | |||
| @@ -175,8 +175,7 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) { | |||
| 175 | { | 175 | { |
| 176 | KScopedSchedulerLock lock(kernel); | 176 | KScopedSchedulerLock lock(kernel); |
| 177 | if (!context.IsThreadWaiting()) { | 177 | if (!context.IsThreadWaiting()) { |
| 178 | context.GetThread().Wakeup(); | 178 | context.GetThread().EndWait(result); |
| 179 | context.GetThread().SetSyncedObject(nullptr, result); | ||
| 180 | } | 179 | } |
| 181 | } | 180 | } |
| 182 | 181 | ||
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp index f168b4f21..e4c5eb74f 100644 --- a/src/core/hle/kernel/k_synchronization_object.cpp +++ b/src/core/hle/kernel/k_synchronization_object.cpp | |||
| @@ -8,11 +8,66 @@ | |||
| 8 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | 8 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" |
| 9 | #include "core/hle/kernel/k_synchronization_object.h" | 9 | #include "core/hle/kernel/k_synchronization_object.h" |
| 10 | #include "core/hle/kernel/k_thread.h" | 10 | #include "core/hle/kernel/k_thread.h" |
| 11 | #include "core/hle/kernel/k_thread_queue.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | 12 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/svc_results.h" | 13 | #include "core/hle/kernel/svc_results.h" |
| 13 | 14 | ||
| 14 | namespace Kernel { | 15 | namespace Kernel { |
| 15 | 16 | ||
| 17 | namespace { | ||
| 18 | |||
| 19 | class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait { | ||
| 20 | public: | ||
| 21 | ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o, | ||
| 22 | KSynchronizationObject::ThreadListNode* n, s32 c) | ||
| 23 | : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {} | ||
| 24 | |||
| 25 | void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, | ||
| 26 | ResultCode wait_result) override { | ||
| 27 | // Determine the sync index, and unlink all nodes. | ||
| 28 | s32 sync_index = -1; | ||
| 29 | for (auto i = 0; i < m_count; ++i) { | ||
| 30 | // Check if this is the signaled object. | ||
| 31 | if (m_objects[i] == signaled_object && sync_index == -1) { | ||
| 32 | sync_index = i; | ||
| 33 | } | ||
| 34 | |||
| 35 | // Unlink the current node from the current object. | ||
| 36 | m_objects[i]->UnlinkNode(std::addressof(m_nodes[i])); | ||
| 37 | } | ||
| 38 | |||
| 39 | // Set the waiting thread's sync index. | ||
| 40 | waiting_thread->SetSyncedIndex(sync_index); | ||
| 41 | |||
| 42 | // Set the waiting thread as not cancellable. | ||
| 43 | waiting_thread->ClearCancellable(); | ||
| 44 | |||
| 45 | // Invoke the base end wait handler. | ||
| 46 | KThreadQueue::EndWait(waiting_thread, wait_result); | ||
| 47 | } | ||
| 48 | |||
| 49 | void CancelWait(KThread* waiting_thread, ResultCode wait_result, | ||
| 50 | bool cancel_timer_task) override { | ||
| 51 | // Remove all nodes from our list. | ||
| 52 | for (auto i = 0; i < m_count; ++i) { | ||
| 53 | m_objects[i]->UnlinkNode(std::addressof(m_nodes[i])); | ||
| 54 | } | ||
| 55 | |||
| 56 | // Set the waiting thread as not cancellable. | ||
| 57 | waiting_thread->ClearCancellable(); | ||
| 58 | |||
| 59 | // Invoke the base cancel wait handler. | ||
| 60 | KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); | ||
| 61 | } | ||
| 62 | |||
| 63 | private: | ||
| 64 | KSynchronizationObject** m_objects; | ||
| 65 | KSynchronizationObject::ThreadListNode* m_nodes; | ||
| 66 | s32 m_count; | ||
| 67 | }; | ||
| 68 | |||
| 69 | } // namespace | ||
| 70 | |||
| 16 | void KSynchronizationObject::Finalize() { | 71 | void KSynchronizationObject::Finalize() { |
| 17 | this->OnFinalizeSynchronizationObject(); | 72 | this->OnFinalizeSynchronizationObject(); |
| 18 | KAutoObject::Finalize(); | 73 | KAutoObject::Finalize(); |
| @@ -25,11 +80,19 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index, | |||
| 25 | std::vector<ThreadListNode> thread_nodes(num_objects); | 80 | std::vector<ThreadListNode> thread_nodes(num_objects); |
| 26 | 81 | ||
| 27 | // Prepare for wait. | 82 | // Prepare for wait. |
| 28 | KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread(); | 83 | KThread* thread = GetCurrentThreadPointer(kernel_ctx); |
| 84 | ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects, | ||
| 85 | thread_nodes.data(), num_objects); | ||
| 29 | 86 | ||
| 30 | { | 87 | { |
| 31 | // Setup the scheduling lock and sleep. | 88 | // Setup the scheduling lock and sleep. |
| 32 | KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout}; | 89 | KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout); |
| 90 | |||
| 91 | // Check if the thread should terminate. | ||
| 92 | if (thread->IsTerminationRequested()) { | ||
| 93 | slp.CancelSleep(); | ||
| 94 | return ResultTerminationRequested; | ||
| 95 | } | ||
| 33 | 96 | ||
| 34 | // Check if any of the objects are already signaled. | 97 | // Check if any of the objects are already signaled. |
| 35 | for (auto i = 0; i < num_objects; ++i) { | 98 | for (auto i = 0; i < num_objects; ++i) { |
| @@ -48,12 +111,6 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index, | |||
| 48 | return ResultTimedOut; | 111 | return ResultTimedOut; |
| 49 | } | 112 | } |
| 50 | 113 | ||
| 51 | // Check if the thread should terminate. | ||
| 52 | if (thread->IsTerminationRequested()) { | ||
| 53 | slp.CancelSleep(); | ||
| 54 | return ResultTerminationRequested; | ||
| 55 | } | ||
| 56 | |||
| 57 | // Check if waiting was canceled. | 114 | // Check if waiting was canceled. |
| 58 | if (thread->IsWaitCancelled()) { | 115 | if (thread->IsWaitCancelled()) { |
| 59 | slp.CancelSleep(); | 116 | slp.CancelSleep(); |
| @@ -66,73 +123,25 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index, | |||
| 66 | thread_nodes[i].thread = thread; | 123 | thread_nodes[i].thread = thread; |
| 67 | thread_nodes[i].next = nullptr; | 124 | thread_nodes[i].next = nullptr; |
| 68 | 125 | ||
| 69 | if (objects[i]->thread_list_tail == nullptr) { | 126 | objects[i]->LinkNode(std::addressof(thread_nodes[i])); |
| 70 | objects[i]->thread_list_head = std::addressof(thread_nodes[i]); | ||
| 71 | } else { | ||
| 72 | objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]); | ||
| 73 | } | ||
| 74 | |||
| 75 | objects[i]->thread_list_tail = std::addressof(thread_nodes[i]); | ||
| 76 | } | 127 | } |
| 77 | 128 | ||
| 78 | // For debugging only | 129 | // Mark the thread as cancellable. |
| 79 | thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)}); | ||
| 80 | |||
| 81 | // Mark the thread as waiting. | ||
| 82 | thread->SetCancellable(); | 130 | thread->SetCancellable(); |
| 83 | thread->SetSyncedObject(nullptr, ResultTimedOut); | ||
| 84 | thread->SetState(ThreadState::Waiting); | ||
| 85 | thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization); | ||
| 86 | } | ||
| 87 | 131 | ||
| 88 | // The lock/sleep is done, so we should be able to get our result. | 132 | // Clear the thread's synced index. |
| 133 | thread->SetSyncedIndex(-1); | ||
| 89 | 134 | ||
| 90 | // Thread is no longer cancellable. | 135 | // Wait for an object to be signaled. |
| 91 | thread->ClearCancellable(); | 136 | thread->BeginWait(std::addressof(wait_queue)); |
| 92 | 137 | thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization); | |
| 93 | // For debugging only | 138 | } |
| 94 | thread->SetWaitObjectsForDebugging({}); | ||
| 95 | 139 | ||
| 96 | // Cancel the timer as needed. | 140 | // Set the output index. |
| 97 | kernel_ctx.TimeManager().UnscheduleTimeEvent(thread); | 141 | *out_index = thread->GetSyncedIndex(); |
| 98 | 142 | ||
| 99 | // Get the wait result. | 143 | // Get the wait result. |
| 100 | ResultCode wait_result{ResultSuccess}; | 144 | return thread->GetWaitResult(); |
| 101 | s32 sync_index = -1; | ||
| 102 | { | ||
| 103 | KScopedSchedulerLock lock(kernel_ctx); | ||
| 104 | KSynchronizationObject* synced_obj; | ||
| 105 | wait_result = thread->GetWaitResult(std::addressof(synced_obj)); | ||
| 106 | |||
| 107 | for (auto i = 0; i < num_objects; ++i) { | ||
| 108 | // Unlink the object from the list. | ||
| 109 | ThreadListNode* prev_ptr = | ||
| 110 | reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head)); | ||
| 111 | ThreadListNode* prev_val = nullptr; | ||
| 112 | ThreadListNode *prev, *tail_prev; | ||
| 113 | |||
| 114 | do { | ||
| 115 | prev = prev_ptr; | ||
| 116 | prev_ptr = prev_ptr->next; | ||
| 117 | tail_prev = prev_val; | ||
| 118 | prev_val = prev_ptr; | ||
| 119 | } while (prev_ptr != std::addressof(thread_nodes[i])); | ||
| 120 | |||
| 121 | if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) { | ||
| 122 | objects[i]->thread_list_tail = tail_prev; | ||
| 123 | } | ||
| 124 | |||
| 125 | prev->next = thread_nodes[i].next; | ||
| 126 | |||
| 127 | if (objects[i] == synced_obj) { | ||
| 128 | sync_index = i; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | // Set output. | ||
| 134 | *out_index = sync_index; | ||
| 135 | return wait_result; | ||
| 136 | } | 145 | } |
| 137 | 146 | ||
| 138 | KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_) | 147 | KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_) |
| @@ -141,7 +150,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_) | |||
| 141 | KSynchronizationObject::~KSynchronizationObject() = default; | 150 | KSynchronizationObject::~KSynchronizationObject() = default; |
| 142 | 151 | ||
| 143 | void KSynchronizationObject::NotifyAvailable(ResultCode result) { | 152 | void KSynchronizationObject::NotifyAvailable(ResultCode result) { |
| 144 | KScopedSchedulerLock lock(kernel); | 153 | KScopedSchedulerLock sl(kernel); |
| 145 | 154 | ||
| 146 | // If we're not signaled, we've nothing to notify. | 155 | // If we're not signaled, we've nothing to notify. |
| 147 | if (!this->IsSignaled()) { | 156 | if (!this->IsSignaled()) { |
| @@ -150,11 +159,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) { | |||
| 150 | 159 | ||
| 151 | // Iterate over each thread. | 160 | // Iterate over each thread. |
| 152 | for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { | 161 | for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { |
| 153 | KThread* thread = cur_node->thread; | 162 | cur_node->thread->NotifyAvailable(this, result); |
| 154 | if (thread->GetState() == ThreadState::Waiting) { | ||
| 155 | thread->SetSyncedObject(this, result); | ||
| 156 | thread->SetState(ThreadState::Runnable); | ||
| 157 | } | ||
| 158 | } | 163 | } |
| 159 | } | 164 | } |
| 160 | 165 | ||
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h index 898e58e16..ec235437b 100644 --- a/src/core/hle/kernel/k_synchronization_object.h +++ b/src/core/hle/kernel/k_synchronization_object.h | |||
| @@ -35,6 +35,38 @@ public: | |||
| 35 | 35 | ||
| 36 | [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const; | 36 | [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const; |
| 37 | 37 | ||
| 38 | void LinkNode(ThreadListNode* node_) { | ||
| 39 | // Link the node to the list. | ||
| 40 | if (thread_list_tail == nullptr) { | ||
| 41 | thread_list_head = node_; | ||
| 42 | } else { | ||
| 43 | thread_list_tail->next = node_; | ||
| 44 | } | ||
| 45 | |||
| 46 | thread_list_tail = node_; | ||
| 47 | } | ||
| 48 | |||
| 49 | void UnlinkNode(ThreadListNode* node_) { | ||
| 50 | // Unlink the node from the list. | ||
| 51 | ThreadListNode* prev_ptr = | ||
| 52 | reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head)); | ||
| 53 | ThreadListNode* prev_val = nullptr; | ||
| 54 | ThreadListNode *prev, *tail_prev; | ||
| 55 | |||
| 56 | do { | ||
| 57 | prev = prev_ptr; | ||
| 58 | prev_ptr = prev_ptr->next; | ||
| 59 | tail_prev = prev_val; | ||
| 60 | prev_val = prev_ptr; | ||
| 61 | } while (prev_ptr != node_); | ||
| 62 | |||
| 63 | if (thread_list_tail == node_) { | ||
| 64 | thread_list_tail = tail_prev; | ||
| 65 | } | ||
| 66 | |||
| 67 | prev->next = node_->next; | ||
| 68 | } | ||
| 69 | |||
| 38 | protected: | 70 | protected: |
| 39 | explicit KSynchronizationObject(KernelCore& kernel); | 71 | explicit KSynchronizationObject(KernelCore& kernel); |
| 40 | ~KSynchronizationObject() override; | 72 | ~KSynchronizationObject() override; |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index db65ce79a..752592e2e 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -13,6 +13,9 @@ | |||
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/fiber.h" | 14 | #include "common/fiber.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "common/scope_exit.h" | ||
| 17 | #include "common/settings.h" | ||
| 18 | #include "common/thread_queue_list.h" | ||
| 16 | #include "core/core.h" | 19 | #include "core/core.h" |
| 17 | #include "core/cpu_manager.h" | 20 | #include "core/cpu_manager.h" |
| 18 | #include "core/hardware_properties.h" | 21 | #include "core/hardware_properties.h" |
| @@ -56,6 +59,34 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, | |||
| 56 | 59 | ||
| 57 | namespace Kernel { | 60 | namespace Kernel { |
| 58 | 61 | ||
| 62 | namespace { | ||
| 63 | |||
| 64 | class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait { | ||
| 65 | public: | ||
| 66 | explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_) | ||
| 67 | : KThreadQueueWithoutEndWait(kernel_) {} | ||
| 68 | }; | ||
| 69 | |||
| 70 | class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue { | ||
| 71 | public: | ||
| 72 | explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl) | ||
| 73 | : KThreadQueue(kernel_), m_wait_list(wl) {} | ||
| 74 | |||
| 75 | void CancelWait(KThread* waiting_thread, ResultCode wait_result, | ||
| 76 | bool cancel_timer_task) override { | ||
| 77 | // Remove the thread from the wait list. | ||
| 78 | m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); | ||
| 79 | |||
| 80 | // Invoke the base cancel wait handler. | ||
| 81 | KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); | ||
| 82 | } | ||
| 83 | |||
| 84 | private: | ||
| 85 | KThread::WaiterList* m_wait_list; | ||
| 86 | }; | ||
| 87 | |||
| 88 | } // namespace | ||
| 89 | |||
| 59 | KThread::KThread(KernelCore& kernel_) | 90 | KThread::KThread(KernelCore& kernel_) |
| 60 | : KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {} | 91 | : KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {} |
| 61 | KThread::~KThread() = default; | 92 | KThread::~KThread() = default; |
| @@ -82,6 +113,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s | |||
| 82 | [[fallthrough]]; | 113 | [[fallthrough]]; |
| 83 | case ThreadType::HighPriority: | 114 | case ThreadType::HighPriority: |
| 84 | [[fallthrough]]; | 115 | [[fallthrough]]; |
| 116 | case ThreadType::Dummy: | ||
| 117 | [[fallthrough]]; | ||
| 85 | case ThreadType::User: | 118 | case ThreadType::User: |
| 86 | ASSERT(((owner == nullptr) || | 119 | ASSERT(((owner == nullptr) || |
| 87 | (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); | 120 | (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); |
| @@ -127,11 +160,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s | |||
| 127 | priority = prio; | 160 | priority = prio; |
| 128 | base_priority = prio; | 161 | base_priority = prio; |
| 129 | 162 | ||
| 130 | // Set sync object and waiting lock to null. | ||
| 131 | synced_object = nullptr; | ||
| 132 | |||
| 133 | // Initialize sleeping queue. | 163 | // Initialize sleeping queue. |
| 134 | sleeping_queue = nullptr; | 164 | wait_queue = nullptr; |
| 135 | 165 | ||
| 136 | // Set suspend flags. | 166 | // Set suspend flags. |
| 137 | suspend_request_flags = 0; | 167 | suspend_request_flags = 0; |
| @@ -184,7 +214,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s | |||
| 184 | // Setup the stack parameters. | 214 | // Setup the stack parameters. |
| 185 | StackParameters& sp = GetStackParameters(); | 215 | StackParameters& sp = GetStackParameters(); |
| 186 | sp.cur_thread = this; | 216 | sp.cur_thread = this; |
| 187 | sp.disable_count = 1; | 217 | sp.disable_count = 0; |
| 188 | SetInExceptionHandler(); | 218 | SetInExceptionHandler(); |
| 189 | 219 | ||
| 190 | // Set thread ID. | 220 | // Set thread ID. |
| @@ -211,15 +241,16 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint | |||
| 211 | // Initialize the thread. | 241 | // Initialize the thread. |
| 212 | R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); | 242 | R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); |
| 213 | 243 | ||
| 214 | // Initialize host context. | 244 | // Initialize emulation parameters. |
| 215 | thread->host_context = | 245 | thread->host_context = |
| 216 | std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); | 246 | std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); |
| 247 | thread->is_single_core = !Settings::values.use_multi_core.GetValue(); | ||
| 217 | 248 | ||
| 218 | return ResultSuccess; | 249 | return ResultSuccess; |
| 219 | } | 250 | } |
| 220 | 251 | ||
| 221 | ResultCode KThread::InitializeDummyThread(KThread* thread) { | 252 | ResultCode KThread::InitializeDummyThread(KThread* thread) { |
| 222 | return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Main); | 253 | return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy); |
| 223 | } | 254 | } |
| 224 | 255 | ||
| 225 | ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { | 256 | ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { |
| @@ -273,11 +304,14 @@ void KThread::Finalize() { | |||
| 273 | 304 | ||
| 274 | auto it = waiter_list.begin(); | 305 | auto it = waiter_list.begin(); |
| 275 | while (it != waiter_list.end()) { | 306 | while (it != waiter_list.end()) { |
| 276 | // The thread shouldn't be a kernel waiter. | 307 | // Clear the lock owner |
| 277 | it->SetLockOwner(nullptr); | 308 | it->SetLockOwner(nullptr); |
| 278 | it->SetSyncedObject(nullptr, ResultInvalidState); | 309 | |
| 279 | it->Wakeup(); | 310 | // Erase the waiter from our list. |
| 280 | it = waiter_list.erase(it); | 311 | it = waiter_list.erase(it); |
| 312 | |||
| 313 | // Cancel the thread's wait. | ||
| 314 | it->CancelWait(ResultInvalidState, true); | ||
| 281 | } | 315 | } |
| 282 | } | 316 | } |
| 283 | 317 | ||
| @@ -294,15 +328,12 @@ bool KThread::IsSignaled() const { | |||
| 294 | return signaled; | 328 | return signaled; |
| 295 | } | 329 | } |
| 296 | 330 | ||
| 297 | void KThread::Wakeup() { | 331 | void KThread::OnTimer() { |
| 298 | KScopedSchedulerLock sl{kernel}; | 332 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 299 | 333 | ||
| 334 | // If we're waiting, cancel the wait. | ||
| 300 | if (GetState() == ThreadState::Waiting) { | 335 | if (GetState() == ThreadState::Waiting) { |
| 301 | if (sleeping_queue != nullptr) { | 336 | wait_queue->CancelWait(this, ResultTimedOut, false); |
| 302 | sleeping_queue->WakeupThread(this); | ||
| 303 | } else { | ||
| 304 | SetState(ThreadState::Runnable); | ||
| 305 | } | ||
| 306 | } | 337 | } |
| 307 | } | 338 | } |
| 308 | 339 | ||
| @@ -327,7 +358,7 @@ void KThread::StartTermination() { | |||
| 327 | 358 | ||
| 328 | // Signal. | 359 | // Signal. |
| 329 | signaled = true; | 360 | signaled = true; |
| 330 | NotifyAvailable(); | 361 | KSynchronizationObject::NotifyAvailable(); |
| 331 | 362 | ||
| 332 | // Clear previous thread in KScheduler. | 363 | // Clear previous thread in KScheduler. |
| 333 | KScheduler::ClearPreviousThread(kernel, this); | 364 | KScheduler::ClearPreviousThread(kernel, this); |
| @@ -475,30 +506,32 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m | |||
| 475 | return ResultSuccess; | 506 | return ResultSuccess; |
| 476 | } | 507 | } |
| 477 | 508 | ||
| 478 | ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) { | 509 | ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) { |
| 479 | ASSERT(parent != nullptr); | 510 | ASSERT(parent != nullptr); |
| 480 | ASSERT(v_affinity_mask != 0); | 511 | ASSERT(v_affinity_mask != 0); |
| 481 | KScopedLightLock lk{activity_pause_lock}; | 512 | KScopedLightLock lk(activity_pause_lock); |
| 482 | 513 | ||
| 483 | // Set the core mask. | 514 | // Set the core mask. |
| 484 | u64 p_affinity_mask = 0; | 515 | u64 p_affinity_mask = 0; |
| 485 | { | 516 | { |
| 486 | KScopedSchedulerLock sl{kernel}; | 517 | KScopedSchedulerLock sl(kernel); |
| 487 | ASSERT(num_core_migration_disables >= 0); | 518 | ASSERT(num_core_migration_disables >= 0); |
| 488 | 519 | ||
| 489 | // If the core id is no-update magic, preserve the ideal core id. | 520 | // If we're updating, set our ideal virtual core. |
| 490 | if (cpu_core_id == Svc::IdealCoreNoUpdate) { | 521 | if (core_id_ != Svc::IdealCoreNoUpdate) { |
| 491 | cpu_core_id = virtual_ideal_core_id; | 522 | virtual_ideal_core_id = core_id_; |
| 492 | R_UNLESS(((1ULL << cpu_core_id) & v_affinity_mask) != 0, ResultInvalidCombination); | 523 | } else { |
| 524 | // Preserve our ideal core id. | ||
| 525 | core_id_ = virtual_ideal_core_id; | ||
| 526 | R_UNLESS(((1ULL << core_id_) & v_affinity_mask) != 0, ResultInvalidCombination); | ||
| 493 | } | 527 | } |
| 494 | 528 | ||
| 495 | // Set the virtual core/affinity mask. | 529 | // Set our affinity mask. |
| 496 | virtual_ideal_core_id = cpu_core_id; | ||
| 497 | virtual_affinity_mask = v_affinity_mask; | 530 | virtual_affinity_mask = v_affinity_mask; |
| 498 | 531 | ||
| 499 | // Translate the virtual core to a physical core. | 532 | // Translate the virtual core to a physical core. |
| 500 | if (cpu_core_id >= 0) { | 533 | if (core_id_ >= 0) { |
| 501 | cpu_core_id = Core::Hardware::VirtualToPhysicalCoreMap[cpu_core_id]; | 534 | core_id_ = Core::Hardware::VirtualToPhysicalCoreMap[core_id_]; |
| 502 | } | 535 | } |
| 503 | 536 | ||
| 504 | // Translate the virtual affinity mask to a physical one. | 537 | // Translate the virtual affinity mask to a physical one. |
| @@ -513,7 +546,7 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) { | |||
| 513 | const KAffinityMask old_mask = physical_affinity_mask; | 546 | const KAffinityMask old_mask = physical_affinity_mask; |
| 514 | 547 | ||
| 515 | // Set our new ideals. | 548 | // Set our new ideals. |
| 516 | physical_ideal_core_id = cpu_core_id; | 549 | physical_ideal_core_id = core_id_; |
| 517 | physical_affinity_mask.SetAffinityMask(p_affinity_mask); | 550 | physical_affinity_mask.SetAffinityMask(p_affinity_mask); |
| 518 | 551 | ||
| 519 | if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { | 552 | if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { |
| @@ -531,18 +564,18 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) { | |||
| 531 | } | 564 | } |
| 532 | } else { | 565 | } else { |
| 533 | // Otherwise, we edit the original affinity for restoration later. | 566 | // Otherwise, we edit the original affinity for restoration later. |
| 534 | original_physical_ideal_core_id = cpu_core_id; | 567 | original_physical_ideal_core_id = core_id_; |
| 535 | original_physical_affinity_mask.SetAffinityMask(p_affinity_mask); | 568 | original_physical_affinity_mask.SetAffinityMask(p_affinity_mask); |
| 536 | } | 569 | } |
| 537 | } | 570 | } |
| 538 | 571 | ||
| 539 | // Update the pinned waiter list. | 572 | // Update the pinned waiter list. |
| 573 | ThreadQueueImplForKThreadSetProperty wait_queue_(kernel, std::addressof(pinned_waiter_list)); | ||
| 540 | { | 574 | { |
| 541 | bool retry_update{}; | 575 | bool retry_update{}; |
| 542 | bool thread_is_pinned{}; | ||
| 543 | do { | 576 | do { |
| 544 | // Lock the scheduler. | 577 | // Lock the scheduler. |
| 545 | KScopedSchedulerLock sl{kernel}; | 578 | KScopedSchedulerLock sl(kernel); |
| 546 | 579 | ||
| 547 | // Don't do any further management if our termination has been requested. | 580 | // Don't do any further management if our termination has been requested. |
| 548 | R_SUCCEED_IF(IsTerminationRequested()); | 581 | R_SUCCEED_IF(IsTerminationRequested()); |
| @@ -570,12 +603,9 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) { | |||
| 570 | R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), | 603 | R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), |
| 571 | ResultTerminationRequested); | 604 | ResultTerminationRequested); |
| 572 | 605 | ||
| 573 | // Note that the thread was pinned. | ||
| 574 | thread_is_pinned = true; | ||
| 575 | |||
| 576 | // Wait until the thread isn't pinned any more. | 606 | // Wait until the thread isn't pinned any more. |
| 577 | pinned_waiter_list.push_back(GetCurrentThread(kernel)); | 607 | pinned_waiter_list.push_back(GetCurrentThread(kernel)); |
| 578 | GetCurrentThread(kernel).SetState(ThreadState::Waiting); | 608 | GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_)); |
| 579 | } else { | 609 | } else { |
| 580 | // If the thread isn't pinned, release the scheduler lock and retry until it's | 610 | // If the thread isn't pinned, release the scheduler lock and retry until it's |
| 581 | // not current. | 611 | // not current. |
| @@ -583,16 +613,6 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) { | |||
| 583 | } | 613 | } |
| 584 | } | 614 | } |
| 585 | } while (retry_update); | 615 | } while (retry_update); |
| 586 | |||
| 587 | // If the thread was pinned, it no longer is, and we should remove the current thread from | ||
| 588 | // our waiter list. | ||
| 589 | if (thread_is_pinned) { | ||
| 590 | // Lock the scheduler. | ||
| 591 | KScopedSchedulerLock sl{kernel}; | ||
| 592 | |||
| 593 | // Remove from the list. | ||
| 594 | pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel))); | ||
| 595 | } | ||
| 596 | } | 616 | } |
| 597 | 617 | ||
| 598 | return ResultSuccess; | 618 | return ResultSuccess; |
| @@ -641,15 +661,9 @@ void KThread::WaitCancel() { | |||
| 641 | KScopedSchedulerLock sl{kernel}; | 661 | KScopedSchedulerLock sl{kernel}; |
| 642 | 662 | ||
| 643 | // Check if we're waiting and cancellable. | 663 | // Check if we're waiting and cancellable. |
| 644 | if (GetState() == ThreadState::Waiting && cancellable) { | 664 | if (this->GetState() == ThreadState::Waiting && cancellable) { |
| 645 | if (sleeping_queue != nullptr) { | 665 | wait_cancelled = false; |
| 646 | sleeping_queue->WakeupThread(this); | 666 | wait_queue->CancelWait(this, ResultCancelled, true); |
| 647 | wait_cancelled = true; | ||
| 648 | } else { | ||
| 649 | SetSyncedObject(nullptr, ResultCancelled); | ||
| 650 | SetState(ThreadState::Runnable); | ||
| 651 | wait_cancelled = false; | ||
| 652 | } | ||
| 653 | } else { | 667 | } else { |
| 654 | // Otherwise, note that we cancelled a wait. | 668 | // Otherwise, note that we cancelled a wait. |
| 655 | wait_cancelled = true; | 669 | wait_cancelled = true; |
| @@ -700,60 +714,59 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { | |||
| 700 | // Set the activity. | 714 | // Set the activity. |
| 701 | { | 715 | { |
| 702 | // Lock the scheduler. | 716 | // Lock the scheduler. |
| 703 | KScopedSchedulerLock sl{kernel}; | 717 | KScopedSchedulerLock sl(kernel); |
| 704 | 718 | ||
| 705 | // Verify our state. | 719 | // Verify our state. |
| 706 | const auto cur_state = GetState(); | 720 | const auto cur_state = this->GetState(); |
| 707 | R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable), | 721 | R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable), |
| 708 | ResultInvalidState); | 722 | ResultInvalidState); |
| 709 | 723 | ||
| 710 | // Either pause or resume. | 724 | // Either pause or resume. |
| 711 | if (activity == Svc::ThreadActivity::Paused) { | 725 | if (activity == Svc::ThreadActivity::Paused) { |
| 712 | // Verify that we're not suspended. | 726 | // Verify that we're not suspended. |
| 713 | R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState); | 727 | R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); |
| 714 | 728 | ||
| 715 | // Suspend. | 729 | // Suspend. |
| 716 | RequestSuspend(SuspendType::Thread); | 730 | this->RequestSuspend(SuspendType::Thread); |
| 717 | } else { | 731 | } else { |
| 718 | ASSERT(activity == Svc::ThreadActivity::Runnable); | 732 | ASSERT(activity == Svc::ThreadActivity::Runnable); |
| 719 | 733 | ||
| 720 | // Verify that we're suspended. | 734 | // Verify that we're suspended. |
| 721 | R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState); | 735 | R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState); |
| 722 | 736 | ||
| 723 | // Resume. | 737 | // Resume. |
| 724 | Resume(SuspendType::Thread); | 738 | this->Resume(SuspendType::Thread); |
| 725 | } | 739 | } |
| 726 | } | 740 | } |
| 727 | 741 | ||
| 728 | // If the thread is now paused, update the pinned waiter list. | 742 | // If the thread is now paused, update the pinned waiter list. |
| 729 | if (activity == Svc::ThreadActivity::Paused) { | 743 | if (activity == Svc::ThreadActivity::Paused) { |
| 730 | bool thread_is_pinned{}; | 744 | ThreadQueueImplForKThreadSetProperty wait_queue_(kernel, |
| 731 | bool thread_is_current{}; | 745 | std::addressof(pinned_waiter_list)); |
| 746 | |||
| 747 | bool thread_is_current; | ||
| 732 | do { | 748 | do { |
| 733 | // Lock the scheduler. | 749 | // Lock the scheduler. |
| 734 | KScopedSchedulerLock sl{kernel}; | 750 | KScopedSchedulerLock sl(kernel); |
| 735 | 751 | ||
| 736 | // Don't do any further management if our termination has been requested. | 752 | // Don't do any further management if our termination has been requested. |
| 737 | R_SUCCEED_IF(IsTerminationRequested()); | 753 | R_SUCCEED_IF(this->IsTerminationRequested()); |
| 754 | |||
| 755 | // By default, treat the thread as not current. | ||
| 756 | thread_is_current = false; | ||
| 738 | 757 | ||
| 739 | // Check whether the thread is pinned. | 758 | // Check whether the thread is pinned. |
| 740 | if (GetStackParameters().is_pinned) { | 759 | if (this->GetStackParameters().is_pinned) { |
| 741 | // Verify that the current thread isn't terminating. | 760 | // Verify that the current thread isn't terminating. |
| 742 | R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), | 761 | R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), |
| 743 | ResultTerminationRequested); | 762 | ResultTerminationRequested); |
| 744 | 763 | ||
| 745 | // Note that the thread was pinned and not current. | ||
| 746 | thread_is_pinned = true; | ||
| 747 | thread_is_current = false; | ||
| 748 | |||
| 749 | // Wait until the thread isn't pinned any more. | 764 | // Wait until the thread isn't pinned any more. |
| 750 | pinned_waiter_list.push_back(GetCurrentThread(kernel)); | 765 | pinned_waiter_list.push_back(GetCurrentThread(kernel)); |
| 751 | GetCurrentThread(kernel).SetState(ThreadState::Waiting); | 766 | GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_)); |
| 752 | } else { | 767 | } else { |
| 753 | // Check if the thread is currently running. | 768 | // Check if the thread is currently running. |
| 754 | // If it is, we'll need to retry. | 769 | // If it is, we'll need to retry. |
| 755 | thread_is_current = false; | ||
| 756 | |||
| 757 | for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { | 770 | for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { |
| 758 | if (kernel.Scheduler(i).GetCurrentThread() == this) { | 771 | if (kernel.Scheduler(i).GetCurrentThread() == this) { |
| 759 | thread_is_current = true; | 772 | thread_is_current = true; |
| @@ -762,16 +775,6 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { | |||
| 762 | } | 775 | } |
| 763 | } | 776 | } |
| 764 | } while (thread_is_current); | 777 | } while (thread_is_current); |
| 765 | |||
| 766 | // If the thread was pinned, it no longer is, and we should remove the current thread from | ||
| 767 | // our waiter list. | ||
| 768 | if (thread_is_pinned) { | ||
| 769 | // Lock the scheduler. | ||
| 770 | KScopedSchedulerLock sl{kernel}; | ||
| 771 | |||
| 772 | // Remove from the list. | ||
| 773 | pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel))); | ||
| 774 | } | ||
| 775 | } | 778 | } |
| 776 | 779 | ||
| 777 | return ResultSuccess; | 780 | return ResultSuccess; |
| @@ -966,6 +969,9 @@ ResultCode KThread::Run() { | |||
| 966 | 969 | ||
| 967 | // Set our state and finish. | 970 | // Set our state and finish. |
| 968 | SetState(ThreadState::Runnable); | 971 | SetState(ThreadState::Runnable); |
| 972 | |||
| 973 | DisableDispatch(); | ||
| 974 | |||
| 969 | return ResultSuccess; | 975 | return ResultSuccess; |
| 970 | } | 976 | } |
| 971 | } | 977 | } |
| @@ -996,27 +1002,61 @@ ResultCode KThread::Sleep(s64 timeout) { | |||
| 996 | ASSERT(this == GetCurrentThreadPointer(kernel)); | 1002 | ASSERT(this == GetCurrentThreadPointer(kernel)); |
| 997 | ASSERT(timeout > 0); | 1003 | ASSERT(timeout > 0); |
| 998 | 1004 | ||
| 1005 | ThreadQueueImplForKThreadSleep wait_queue_(kernel); | ||
| 999 | { | 1006 | { |
| 1000 | // Setup the scheduling lock and sleep. | 1007 | // Setup the scheduling lock and sleep. |
| 1001 | KScopedSchedulerLockAndSleep slp{kernel, this, timeout}; | 1008 | KScopedSchedulerLockAndSleep slp(kernel, this, timeout); |
| 1002 | 1009 | ||
| 1003 | // Check if the thread should terminate. | 1010 | // Check if the thread should terminate. |
| 1004 | if (IsTerminationRequested()) { | 1011 | if (this->IsTerminationRequested()) { |
| 1005 | slp.CancelSleep(); | 1012 | slp.CancelSleep(); |
| 1006 | return ResultTerminationRequested; | 1013 | return ResultTerminationRequested; |
| 1007 | } | 1014 | } |
| 1008 | 1015 | ||
| 1009 | // Mark the thread as waiting. | 1016 | // Wait for the sleep to end. |
| 1010 | SetState(ThreadState::Waiting); | 1017 | this->BeginWait(std::addressof(wait_queue_)); |
| 1011 | SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); | 1018 | SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); |
| 1012 | } | 1019 | } |
| 1013 | 1020 | ||
| 1014 | // The lock/sleep is done. | 1021 | return ResultSuccess; |
| 1022 | } | ||
| 1015 | 1023 | ||
| 1016 | // Cancel the timer. | 1024 | void KThread::BeginWait(KThreadQueue* queue) { |
| 1017 | kernel.TimeManager().UnscheduleTimeEvent(this); | 1025 | // Set our state as waiting. |
| 1026 | SetState(ThreadState::Waiting); | ||
| 1018 | 1027 | ||
| 1019 | return ResultSuccess; | 1028 | // Set our wait queue. |
| 1029 | wait_queue = queue; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) { | ||
| 1033 | // Lock the scheduler. | ||
| 1034 | KScopedSchedulerLock sl(kernel); | ||
| 1035 | |||
| 1036 | // If we're waiting, notify our queue that we're available. | ||
| 1037 | if (GetState() == ThreadState::Waiting) { | ||
| 1038 | wait_queue->NotifyAvailable(this, signaled_object, wait_result_); | ||
| 1039 | } | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | void KThread::EndWait(ResultCode wait_result_) { | ||
| 1043 | // Lock the scheduler. | ||
| 1044 | KScopedSchedulerLock sl(kernel); | ||
| 1045 | |||
| 1046 | // If we're waiting, notify our queue that we're available. | ||
| 1047 | if (GetState() == ThreadState::Waiting) { | ||
| 1048 | wait_queue->EndWait(this, wait_result_); | ||
| 1049 | } | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) { | ||
| 1053 | // Lock the scheduler. | ||
| 1054 | KScopedSchedulerLock sl(kernel); | ||
| 1055 | |||
| 1056 | // If we're waiting, notify our queue that we're available. | ||
| 1057 | if (GetState() == ThreadState::Waiting) { | ||
| 1058 | wait_queue->CancelWait(this, wait_result_, cancel_timer_task); | ||
| 1059 | } | ||
| 1020 | } | 1060 | } |
| 1021 | 1061 | ||
| 1022 | void KThread::SetState(ThreadState state) { | 1062 | void KThread::SetState(ThreadState state) { |
| @@ -1050,4 +1090,26 @@ s32 GetCurrentCoreId(KernelCore& kernel) { | |||
| 1050 | return GetCurrentThread(kernel).GetCurrentCore(); | 1090 | return GetCurrentThread(kernel).GetCurrentCore(); |
| 1051 | } | 1091 | } |
| 1052 | 1092 | ||
| 1093 | KScopedDisableDispatch::~KScopedDisableDispatch() { | ||
| 1094 | // If we are shutting down the kernel, none of this is relevant anymore. | ||
| 1095 | if (kernel.IsShuttingDown()) { | ||
| 1096 | return; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | // Skip the reschedule if single-core, as dispatch tracking is disabled here. | ||
| 1100 | if (!Settings::values.use_multi_core.GetValue()) { | ||
| 1101 | return; | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) { | ||
| 1105 | auto scheduler = kernel.CurrentScheduler(); | ||
| 1106 | |||
| 1107 | if (scheduler) { | ||
| 1108 | scheduler->RescheduleCurrentCore(); | ||
| 1109 | } | ||
| 1110 | } else { | ||
| 1111 | GetCurrentThread(kernel).EnableDispatch(); | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | |||
| 1053 | } // namespace Kernel | 1115 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index c77f44ad4..c8a08bd71 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -48,6 +48,7 @@ enum class ThreadType : u32 { | |||
| 48 | Kernel = 1, | 48 | Kernel = 1, |
| 49 | HighPriority = 2, | 49 | HighPriority = 2, |
| 50 | User = 3, | 50 | User = 3, |
| 51 | Dummy = 100, // Special thread type for emulation purposes only | ||
| 51 | }; | 52 | }; |
| 52 | DECLARE_ENUM_FLAG_OPERATORS(ThreadType); | 53 | DECLARE_ENUM_FLAG_OPERATORS(ThreadType); |
| 53 | 54 | ||
| @@ -161,8 +162,6 @@ public: | |||
| 161 | } | 162 | } |
| 162 | } | 163 | } |
| 163 | 164 | ||
| 164 | void Wakeup(); | ||
| 165 | |||
| 166 | void SetBasePriority(s32 value); | 165 | void SetBasePriority(s32 value); |
| 167 | 166 | ||
| 168 | [[nodiscard]] ResultCode Run(); | 167 | [[nodiscard]] ResultCode Run(); |
| @@ -197,13 +196,19 @@ public: | |||
| 197 | 196 | ||
| 198 | void Suspend(); | 197 | void Suspend(); |
| 199 | 198 | ||
| 200 | void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) { | 199 | constexpr void SetSyncedIndex(s32 index) { |
| 201 | synced_object = obj; | 200 | synced_index = index; |
| 201 | } | ||
| 202 | |||
| 203 | [[nodiscard]] constexpr s32 GetSyncedIndex() const { | ||
| 204 | return synced_index; | ||
| 205 | } | ||
| 206 | |||
| 207 | constexpr void SetWaitResult(ResultCode wait_res) { | ||
| 202 | wait_result = wait_res; | 208 | wait_result = wait_res; |
| 203 | } | 209 | } |
| 204 | 210 | ||
| 205 | [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const { | 211 | [[nodiscard]] constexpr ResultCode GetWaitResult() const { |
| 206 | *out = synced_object; | ||
| 207 | return wait_result; | 212 | return wait_result; |
| 208 | } | 213 | } |
| 209 | 214 | ||
| @@ -374,6 +379,8 @@ public: | |||
| 374 | 379 | ||
| 375 | [[nodiscard]] bool IsSignaled() const override; | 380 | [[nodiscard]] bool IsSignaled() const override; |
| 376 | 381 | ||
| 382 | void OnTimer(); | ||
| 383 | |||
| 377 | static void PostDestroy(uintptr_t arg); | 384 | static void PostDestroy(uintptr_t arg); |
| 378 | 385 | ||
| 379 | [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); | 386 | [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); |
| @@ -446,20 +453,39 @@ public: | |||
| 446 | return per_core_priority_queue_entry[core]; | 453 | return per_core_priority_queue_entry[core]; |
| 447 | } | 454 | } |
| 448 | 455 | ||
| 449 | void SetSleepingQueue(KThreadQueue* q) { | 456 | [[nodiscard]] bool IsKernelThread() const { |
| 450 | sleeping_queue = q; | 457 | return GetActiveCore() == 3; |
| 458 | } | ||
| 459 | |||
| 460 | [[nodiscard]] bool IsDispatchTrackingDisabled() const { | ||
| 461 | return is_single_core || IsKernelThread(); | ||
| 451 | } | 462 | } |
| 452 | 463 | ||
| 453 | [[nodiscard]] s32 GetDisableDispatchCount() const { | 464 | [[nodiscard]] s32 GetDisableDispatchCount() const { |
| 465 | if (IsDispatchTrackingDisabled()) { | ||
| 466 | // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. | ||
| 467 | return 1; | ||
| 468 | } | ||
| 469 | |||
| 454 | return this->GetStackParameters().disable_count; | 470 | return this->GetStackParameters().disable_count; |
| 455 | } | 471 | } |
| 456 | 472 | ||
| 457 | void DisableDispatch() { | 473 | void DisableDispatch() { |
| 474 | if (IsDispatchTrackingDisabled()) { | ||
| 475 | // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. | ||
| 476 | return; | ||
| 477 | } | ||
| 478 | |||
| 458 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); | 479 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); |
| 459 | this->GetStackParameters().disable_count++; | 480 | this->GetStackParameters().disable_count++; |
| 460 | } | 481 | } |
| 461 | 482 | ||
| 462 | void EnableDispatch() { | 483 | void EnableDispatch() { |
| 484 | if (IsDispatchTrackingDisabled()) { | ||
| 485 | // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. | ||
| 486 | return; | ||
| 487 | } | ||
| 488 | |||
| 463 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); | 489 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); |
| 464 | this->GetStackParameters().disable_count--; | 490 | this->GetStackParameters().disable_count--; |
| 465 | } | 491 | } |
| @@ -573,6 +599,15 @@ public: | |||
| 573 | address_key_value = val; | 599 | address_key_value = val; |
| 574 | } | 600 | } |
| 575 | 601 | ||
| 602 | void ClearWaitQueue() { | ||
| 603 | wait_queue = nullptr; | ||
| 604 | } | ||
| 605 | |||
| 606 | void BeginWait(KThreadQueue* queue); | ||
| 607 | void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_); | ||
| 608 | void EndWait(ResultCode wait_result_); | ||
| 609 | void CancelWait(ResultCode wait_result_, bool cancel_timer_task); | ||
| 610 | |||
| 576 | [[nodiscard]] bool HasWaiters() const { | 611 | [[nodiscard]] bool HasWaiters() const { |
| 577 | return !waiter_list.empty(); | 612 | return !waiter_list.empty(); |
| 578 | } | 613 | } |
| @@ -667,7 +702,6 @@ private: | |||
| 667 | KAffinityMask physical_affinity_mask{}; | 702 | KAffinityMask physical_affinity_mask{}; |
| 668 | u64 thread_id{}; | 703 | u64 thread_id{}; |
| 669 | std::atomic<s64> cpu_time{}; | 704 | std::atomic<s64> cpu_time{}; |
| 670 | KSynchronizationObject* synced_object{}; | ||
| 671 | VAddr address_key{}; | 705 | VAddr address_key{}; |
| 672 | KProcess* parent{}; | 706 | KProcess* parent{}; |
| 673 | VAddr kernel_stack_top{}; | 707 | VAddr kernel_stack_top{}; |
| @@ -677,13 +711,14 @@ private: | |||
| 677 | s64 schedule_count{}; | 711 | s64 schedule_count{}; |
| 678 | s64 last_scheduled_tick{}; | 712 | s64 last_scheduled_tick{}; |
| 679 | std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; | 713 | std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; |
| 680 | KThreadQueue* sleeping_queue{}; | 714 | KThreadQueue* wait_queue{}; |
| 681 | WaiterList waiter_list{}; | 715 | WaiterList waiter_list{}; |
| 682 | WaiterList pinned_waiter_list{}; | 716 | WaiterList pinned_waiter_list{}; |
| 683 | KThread* lock_owner{}; | 717 | KThread* lock_owner{}; |
| 684 | u32 address_key_value{}; | 718 | u32 address_key_value{}; |
| 685 | u32 suspend_request_flags{}; | 719 | u32 suspend_request_flags{}; |
| 686 | u32 suspend_allowed_flags{}; | 720 | u32 suspend_allowed_flags{}; |
| 721 | s32 synced_index{}; | ||
| 687 | ResultCode wait_result{ResultSuccess}; | 722 | ResultCode wait_result{ResultSuccess}; |
| 688 | s32 base_priority{}; | 723 | s32 base_priority{}; |
| 689 | s32 physical_ideal_core_id{}; | 724 | s32 physical_ideal_core_id{}; |
| @@ -708,6 +743,7 @@ private: | |||
| 708 | 743 | ||
| 709 | // For emulation | 744 | // For emulation |
| 710 | std::shared_ptr<Common::Fiber> host_context{}; | 745 | std::shared_ptr<Common::Fiber> host_context{}; |
| 746 | bool is_single_core{}; | ||
| 711 | 747 | ||
| 712 | // For debugging | 748 | // For debugging |
| 713 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; | 749 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; |
| @@ -752,4 +788,20 @@ public: | |||
| 752 | } | 788 | } |
| 753 | }; | 789 | }; |
| 754 | 790 | ||
| 791 | class KScopedDisableDispatch { | ||
| 792 | public: | ||
| 793 | [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} { | ||
| 794 | // If we are shutting down the kernel, none of this is relevant anymore. | ||
| 795 | if (kernel.IsShuttingDown()) { | ||
| 796 | return; | ||
| 797 | } | ||
| 798 | GetCurrentThread(kernel).DisableDispatch(); | ||
| 799 | } | ||
| 800 | |||
| 801 | ~KScopedDisableDispatch(); | ||
| 802 | |||
| 803 | private: | ||
| 804 | KernelCore& kernel; | ||
| 805 | }; | ||
| 806 | |||
| 755 | } // namespace Kernel | 807 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp new file mode 100644 index 000000000..d5248b547 --- /dev/null +++ b/src/core/hle/kernel/k_thread_queue.cpp | |||
| @@ -0,0 +1,49 @@ | |||
| 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/hle/kernel/k_thread_queue.h" | ||
| 6 | #include "core/hle/kernel/kernel.h" | ||
| 7 | #include "core/hle/kernel/time_manager.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread, | ||
| 12 | [[maybe_unused]] KSynchronizationObject* signaled_object, | ||
| 13 | [[maybe_unused]] ResultCode wait_result) {} | ||
| 14 | |||
| 15 | void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) { | ||
| 16 | // Set the thread's wait result. | ||
| 17 | waiting_thread->SetWaitResult(wait_result); | ||
| 18 | |||
| 19 | // Set the thread as runnable. | ||
| 20 | waiting_thread->SetState(ThreadState::Runnable); | ||
| 21 | |||
| 22 | // Clear the thread's wait queue. | ||
| 23 | waiting_thread->ClearWaitQueue(); | ||
| 24 | |||
| 25 | // Cancel the thread task. | ||
| 26 | kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); | ||
| 27 | } | ||
| 28 | |||
| 29 | void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result, | ||
| 30 | bool cancel_timer_task) { | ||
| 31 | // Set the thread's wait result. | ||
| 32 | waiting_thread->SetWaitResult(wait_result); | ||
| 33 | |||
| 34 | // Set the thread as runnable. | ||
| 35 | waiting_thread->SetState(ThreadState::Runnable); | ||
| 36 | |||
| 37 | // Clear the thread's wait queue. | ||
| 38 | waiting_thread->ClearWaitQueue(); | ||
| 39 | |||
| 40 | // Cancel the thread task. | ||
| 41 | if (cancel_timer_task) { | ||
| 42 | kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread, | ||
| 47 | [[maybe_unused]] ResultCode wait_result) {} | ||
| 48 | |||
| 49 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h index 35d471dc5..ccb718e49 100644 --- a/src/core/hle/kernel/k_thread_queue.h +++ b/src/core/hle/kernel/k_thread_queue.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/kernel/k_scheduler.h" | ||
| 7 | #include "core/hle/kernel/k_thread.h" | 8 | #include "core/hle/kernel/k_thread.h" |
| 8 | 9 | ||
| 9 | namespace Kernel { | 10 | namespace Kernel { |
| @@ -11,71 +12,24 @@ namespace Kernel { | |||
| 11 | class KThreadQueue { | 12 | class KThreadQueue { |
| 12 | public: | 13 | public: |
| 13 | explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {} | 14 | explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {} |
| 15 | virtual ~KThreadQueue() = default; | ||
| 14 | 16 | ||
| 15 | bool IsEmpty() const { | 17 | virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object, |
| 16 | return wait_list.empty(); | 18 | ResultCode wait_result); |
| 17 | } | 19 | virtual void EndWait(KThread* waiting_thread, ResultCode wait_result); |
| 18 | 20 | virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result, | |
| 19 | KThread::WaiterList::iterator begin() { | 21 | bool cancel_timer_task); |
| 20 | return wait_list.begin(); | ||
| 21 | } | ||
| 22 | KThread::WaiterList::iterator end() { | ||
| 23 | return wait_list.end(); | ||
| 24 | } | ||
| 25 | |||
| 26 | bool SleepThread(KThread* t) { | ||
| 27 | KScopedSchedulerLock sl{kernel}; | ||
| 28 | |||
| 29 | // If the thread needs terminating, don't enqueue it. | ||
| 30 | if (t->IsTerminationRequested()) { | ||
| 31 | return false; | ||
| 32 | } | ||
| 33 | |||
| 34 | // Set the thread's queue and mark it as waiting. | ||
| 35 | t->SetSleepingQueue(this); | ||
| 36 | t->SetState(ThreadState::Waiting); | ||
| 37 | |||
| 38 | // Add the thread to the queue. | ||
| 39 | wait_list.push_back(*t); | ||
| 40 | |||
| 41 | return true; | ||
| 42 | } | ||
| 43 | |||
| 44 | void WakeupThread(KThread* t) { | ||
| 45 | KScopedSchedulerLock sl{kernel}; | ||
| 46 | |||
| 47 | // Remove the thread from the queue. | ||
| 48 | wait_list.erase(wait_list.iterator_to(*t)); | ||
| 49 | |||
| 50 | // Mark the thread as no longer sleeping. | ||
| 51 | t->SetState(ThreadState::Runnable); | ||
| 52 | t->SetSleepingQueue(nullptr); | ||
| 53 | } | ||
| 54 | |||
| 55 | KThread* WakeupFrontThread() { | ||
| 56 | KScopedSchedulerLock sl{kernel}; | ||
| 57 | |||
| 58 | if (wait_list.empty()) { | ||
| 59 | return nullptr; | ||
| 60 | } else { | ||
| 61 | // Remove the thread from the queue. | ||
| 62 | auto it = wait_list.begin(); | ||
| 63 | KThread* thread = std::addressof(*it); | ||
| 64 | wait_list.erase(it); | ||
| 65 | |||
| 66 | ASSERT(thread->GetState() == ThreadState::Waiting); | ||
| 67 | |||
| 68 | // Mark the thread as no longer sleeping. | ||
| 69 | thread->SetState(ThreadState::Runnable); | ||
| 70 | thread->SetSleepingQueue(nullptr); | ||
| 71 | |||
| 72 | return thread; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | 22 | ||
| 76 | private: | 23 | private: |
| 77 | KernelCore& kernel; | 24 | KernelCore& kernel; |
| 78 | KThread::WaiterList wait_list{}; | 25 | KThread::WaiterList wait_list{}; |
| 79 | }; | 26 | }; |
| 80 | 27 | ||
| 28 | class KThreadQueueWithoutEndWait : public KThreadQueue { | ||
| 29 | public: | ||
| 30 | explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {} | ||
| 31 | |||
| 32 | void EndWait(KThread* waiting_thread, ResultCode wait_result) override final; | ||
| 33 | }; | ||
| 34 | |||
| 81 | } // namespace Kernel | 35 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e42a6d36f..2e4e4cb1c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "common/microprofile.h" | 16 | #include "common/microprofile.h" |
| 17 | #include "common/scope_exit.h" | ||
| 17 | #include "common/thread.h" | 18 | #include "common/thread.h" |
| 18 | #include "common/thread_worker.h" | 19 | #include "common/thread_worker.h" |
| 19 | #include "core/arm/arm_interface.h" | 20 | #include "core/arm/arm_interface.h" |
| @@ -83,12 +84,16 @@ struct KernelCore::Impl { | |||
| 83 | } | 84 | } |
| 84 | 85 | ||
| 85 | void InitializeCores() { | 86 | void InitializeCores() { |
| 86 | for (auto& core : cores) { | 87 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { |
| 87 | core.Initialize(current_process->Is64BitProcess()); | 88 | cores[core_id].Initialize(current_process->Is64BitProcess()); |
| 89 | system.Memory().SetCurrentPageTable(*current_process, core_id); | ||
| 88 | } | 90 | } |
| 89 | } | 91 | } |
| 90 | 92 | ||
| 91 | void Shutdown() { | 93 | void Shutdown() { |
| 94 | is_shutting_down.store(true, std::memory_order_relaxed); | ||
| 95 | SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); | ||
| 96 | |||
| 92 | process_list.clear(); | 97 | process_list.clear(); |
| 93 | 98 | ||
| 94 | // Close all open server ports. | 99 | // Close all open server ports. |
| @@ -123,15 +128,6 @@ struct KernelCore::Impl { | |||
| 123 | next_user_process_id = KProcess::ProcessIDMin; | 128 | next_user_process_id = KProcess::ProcessIDMin; |
| 124 | next_thread_id = 1; | 129 | next_thread_id = 1; |
| 125 | 130 | ||
| 126 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | ||
| 127 | if (suspend_threads[core_id]) { | ||
| 128 | suspend_threads[core_id]->Close(); | ||
| 129 | suspend_threads[core_id] = nullptr; | ||
| 130 | } | ||
| 131 | |||
| 132 | schedulers[core_id].reset(); | ||
| 133 | } | ||
| 134 | |||
| 135 | cores.clear(); | 131 | cores.clear(); |
| 136 | 132 | ||
| 137 | global_handle_table->Finalize(); | 133 | global_handle_table->Finalize(); |
| @@ -159,6 +155,16 @@ struct KernelCore::Impl { | |||
| 159 | CleanupObject(time_shared_mem); | 155 | CleanupObject(time_shared_mem); |
| 160 | CleanupObject(system_resource_limit); | 156 | CleanupObject(system_resource_limit); |
| 161 | 157 | ||
| 158 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | ||
| 159 | if (suspend_threads[core_id]) { | ||
| 160 | suspend_threads[core_id]->Close(); | ||
| 161 | suspend_threads[core_id] = nullptr; | ||
| 162 | } | ||
| 163 | |||
| 164 | schedulers[core_id]->Finalize(); | ||
| 165 | schedulers[core_id].reset(); | ||
| 166 | } | ||
| 167 | |||
| 162 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others | 168 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others |
| 163 | next_host_thread_id = Core::Hardware::NUM_CPU_CORES; | 169 | next_host_thread_id = Core::Hardware::NUM_CPU_CORES; |
| 164 | 170 | ||
| @@ -245,13 +251,11 @@ struct KernelCore::Impl { | |||
| 245 | KScopedSchedulerLock lock(kernel); | 251 | KScopedSchedulerLock lock(kernel); |
| 246 | global_scheduler_context->PreemptThreads(); | 252 | global_scheduler_context->PreemptThreads(); |
| 247 | } | 253 | } |
| 248 | const auto time_interval = std::chrono::nanoseconds{ | 254 | const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; |
| 249 | Core::Timing::msToCycles(std::chrono::milliseconds(10))}; | ||
| 250 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 255 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 251 | }); | 256 | }); |
| 252 | 257 | ||
| 253 | const auto time_interval = | 258 | const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)}; |
| 254 | std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))}; | ||
| 255 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 259 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 256 | } | 260 | } |
| 257 | 261 | ||
| @@ -267,14 +271,6 @@ struct KernelCore::Impl { | |||
| 267 | 271 | ||
| 268 | void MakeCurrentProcess(KProcess* process) { | 272 | void MakeCurrentProcess(KProcess* process) { |
| 269 | current_process = process; | 273 | current_process = process; |
| 270 | if (process == nullptr) { | ||
| 271 | return; | ||
| 272 | } | ||
| 273 | |||
| 274 | const u32 core_id = GetCurrentHostThreadID(); | ||
| 275 | if (core_id < Core::Hardware::NUM_CPU_CORES) { | ||
| 276 | system.Memory().SetCurrentPageTable(*process, core_id); | ||
| 277 | } | ||
| 278 | } | 274 | } |
| 279 | 275 | ||
| 280 | static inline thread_local u32 host_thread_id = UINT32_MAX; | 276 | static inline thread_local u32 host_thread_id = UINT32_MAX; |
| @@ -300,15 +296,16 @@ struct KernelCore::Impl { | |||
| 300 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time | 296 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time |
| 301 | KThread* GetHostDummyThread() { | 297 | KThread* GetHostDummyThread() { |
| 302 | auto make_thread = [this]() { | 298 | auto make_thread = [this]() { |
| 303 | std::unique_ptr<KThread> thread = std::make_unique<KThread>(system.Kernel()); | 299 | std::lock_guard lk(dummy_thread_lock); |
| 300 | auto& thread = dummy_threads.emplace_back(std::make_unique<KThread>(system.Kernel())); | ||
| 304 | KAutoObject::Create(thread.get()); | 301 | KAutoObject::Create(thread.get()); |
| 305 | ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess()); | 302 | ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess()); |
| 306 | thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); | 303 | thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); |
| 307 | return thread; | 304 | return thread.get(); |
| 308 | }; | 305 | }; |
| 309 | 306 | ||
| 310 | thread_local auto thread = make_thread(); | 307 | thread_local KThread* saved_thread = make_thread(); |
| 311 | return thread.get(); | 308 | return saved_thread; |
| 312 | } | 309 | } |
| 313 | 310 | ||
| 314 | /// Registers a CPU core thread by allocating a host thread ID for it | 311 | /// Registers a CPU core thread by allocating a host thread ID for it |
| @@ -343,7 +340,16 @@ struct KernelCore::Impl { | |||
| 343 | is_phantom_mode_for_singlecore = value; | 340 | is_phantom_mode_for_singlecore = value; |
| 344 | } | 341 | } |
| 345 | 342 | ||
| 343 | bool IsShuttingDown() const { | ||
| 344 | return is_shutting_down.load(std::memory_order_relaxed); | ||
| 345 | } | ||
| 346 | |||
| 346 | KThread* GetCurrentEmuThread() { | 347 | KThread* GetCurrentEmuThread() { |
| 348 | // If we are shutting down the kernel, none of this is relevant anymore. | ||
| 349 | if (IsShuttingDown()) { | ||
| 350 | return {}; | ||
| 351 | } | ||
| 352 | |||
| 347 | const auto thread_id = GetCurrentHostThreadID(); | 353 | const auto thread_id = GetCurrentHostThreadID(); |
| 348 | if (thread_id >= Core::Hardware::NUM_CPU_CORES) { | 354 | if (thread_id >= Core::Hardware::NUM_CPU_CORES) { |
| 349 | return GetHostDummyThread(); | 355 | return GetHostDummyThread(); |
| @@ -695,6 +701,12 @@ struct KernelCore::Impl { | |||
| 695 | return port; | 701 | return port; |
| 696 | } | 702 | } |
| 697 | 703 | ||
| 704 | std::mutex server_ports_lock; | ||
| 705 | std::mutex server_sessions_lock; | ||
| 706 | std::mutex registered_objects_lock; | ||
| 707 | std::mutex registered_in_use_objects_lock; | ||
| 708 | std::mutex dummy_thread_lock; | ||
| 709 | |||
| 698 | std::atomic<u32> next_object_id{0}; | 710 | std::atomic<u32> next_object_id{0}; |
| 699 | std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; | 711 | std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; |
| 700 | std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; | 712 | std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; |
| @@ -725,10 +737,6 @@ struct KernelCore::Impl { | |||
| 725 | std::unordered_set<KServerSession*> server_sessions; | 737 | std::unordered_set<KServerSession*> server_sessions; |
| 726 | std::unordered_set<KAutoObject*> registered_objects; | 738 | std::unordered_set<KAutoObject*> registered_objects; |
| 727 | std::unordered_set<KAutoObject*> registered_in_use_objects; | 739 | std::unordered_set<KAutoObject*> registered_in_use_objects; |
| 728 | std::mutex server_ports_lock; | ||
| 729 | std::mutex server_sessions_lock; | ||
| 730 | std::mutex registered_objects_lock; | ||
| 731 | std::mutex registered_in_use_objects_lock; | ||
| 732 | 740 | ||
| 733 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; | 741 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; |
| 734 | std::vector<Kernel::PhysicalCore> cores; | 742 | std::vector<Kernel::PhysicalCore> cores; |
| @@ -753,7 +761,11 @@ struct KernelCore::Impl { | |||
| 753 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; | 761 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; |
| 754 | std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; | 762 | std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; |
| 755 | 763 | ||
| 764 | // Specifically tracked to be automatically destroyed with kernel | ||
| 765 | std::vector<std::unique_ptr<KThread>> dummy_threads; | ||
| 766 | |||
| 756 | bool is_multicore{}; | 767 | bool is_multicore{}; |
| 768 | std::atomic_bool is_shutting_down{}; | ||
| 757 | bool is_phantom_mode_for_singlecore{}; | 769 | bool is_phantom_mode_for_singlecore{}; |
| 758 | u32 single_core_thread_id{}; | 770 | u32 single_core_thread_id{}; |
| 759 | 771 | ||
| @@ -839,16 +851,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | |||
| 839 | return impl->cores[id]; | 851 | return impl->cores[id]; |
| 840 | } | 852 | } |
| 841 | 853 | ||
| 854 | size_t KernelCore::CurrentPhysicalCoreIndex() const { | ||
| 855 | const u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 856 | if (core_id >= Core::Hardware::NUM_CPU_CORES) { | ||
| 857 | return Core::Hardware::NUM_CPU_CORES - 1; | ||
| 858 | } | ||
| 859 | return core_id; | ||
| 860 | } | ||
| 861 | |||
| 842 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { | 862 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { |
| 843 | u32 core_id = impl->GetCurrentHostThreadID(); | 863 | return impl->cores[CurrentPhysicalCoreIndex()]; |
| 844 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 845 | return impl->cores[core_id]; | ||
| 846 | } | 864 | } |
| 847 | 865 | ||
| 848 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { | 866 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { |
| 849 | u32 core_id = impl->GetCurrentHostThreadID(); | 867 | return impl->cores[CurrentPhysicalCoreIndex()]; |
| 850 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 851 | return impl->cores[core_id]; | ||
| 852 | } | 868 | } |
| 853 | 869 | ||
| 854 | Kernel::KScheduler* KernelCore::CurrentScheduler() { | 870 | Kernel::KScheduler* KernelCore::CurrentScheduler() { |
| @@ -1051,6 +1067,9 @@ void KernelCore::Suspend(bool in_suspention) { | |||
| 1051 | impl->suspend_threads[core_id]->SetState(state); | 1067 | impl->suspend_threads[core_id]->SetState(state); |
| 1052 | impl->suspend_threads[core_id]->SetWaitReasonForDebugging( | 1068 | impl->suspend_threads[core_id]->SetWaitReasonForDebugging( |
| 1053 | ThreadWaitReasonForDebugging::Suspended); | 1069 | ThreadWaitReasonForDebugging::Suspended); |
| 1070 | if (!should_suspend) { | ||
| 1071 | impl->suspend_threads[core_id]->DisableDispatch(); | ||
| 1072 | } | ||
| 1054 | } | 1073 | } |
| 1055 | } | 1074 | } |
| 1056 | } | 1075 | } |
| @@ -1059,19 +1078,21 @@ bool KernelCore::IsMulticore() const { | |||
| 1059 | return impl->is_multicore; | 1078 | return impl->is_multicore; |
| 1060 | } | 1079 | } |
| 1061 | 1080 | ||
| 1081 | bool KernelCore::IsShuttingDown() const { | ||
| 1082 | return impl->IsShuttingDown(); | ||
| 1083 | } | ||
| 1084 | |||
| 1062 | void KernelCore::ExceptionalExit() { | 1085 | void KernelCore::ExceptionalExit() { |
| 1063 | exception_exited = true; | 1086 | exception_exited = true; |
| 1064 | Suspend(true); | 1087 | Suspend(true); |
| 1065 | } | 1088 | } |
| 1066 | 1089 | ||
| 1067 | void KernelCore::EnterSVCProfile() { | 1090 | void KernelCore::EnterSVCProfile() { |
| 1068 | std::size_t core = impl->GetCurrentHostThreadID(); | 1091 | impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); |
| 1069 | impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); | ||
| 1070 | } | 1092 | } |
| 1071 | 1093 | ||
| 1072 | void KernelCore::ExitSVCProfile() { | 1094 | void KernelCore::ExitSVCProfile() { |
| 1073 | std::size_t core = impl->GetCurrentHostThreadID(); | 1095 | MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]); |
| 1074 | MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); | ||
| 1075 | } | 1096 | } |
| 1076 | 1097 | ||
| 1077 | std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { | 1098 | std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index d2ceae950..b9b423908 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -53,6 +53,7 @@ class KSharedMemoryInfo; | |||
| 53 | class KThread; | 53 | class KThread; |
| 54 | class KTransferMemory; | 54 | class KTransferMemory; |
| 55 | class KWritableEvent; | 55 | class KWritableEvent; |
| 56 | class KCodeMemory; | ||
| 56 | class PhysicalCore; | 57 | class PhysicalCore; |
| 57 | class ServiceThread; | 58 | class ServiceThread; |
| 58 | class Synchronization; | 59 | class Synchronization; |
| @@ -148,6 +149,9 @@ public: | |||
| 148 | /// Gets the an instance of the respective physical CPU core. | 149 | /// Gets the an instance of the respective physical CPU core. |
| 149 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; | 150 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; |
| 150 | 151 | ||
| 152 | /// Gets the current physical core index for the running host thread. | ||
| 153 | std::size_t CurrentPhysicalCoreIndex() const; | ||
| 154 | |||
| 151 | /// Gets the sole instance of the Scheduler at the current running core. | 155 | /// Gets the sole instance of the Scheduler at the current running core. |
| 152 | Kernel::KScheduler* CurrentScheduler(); | 156 | Kernel::KScheduler* CurrentScheduler(); |
| 153 | 157 | ||
| @@ -271,6 +275,8 @@ public: | |||
| 271 | 275 | ||
| 272 | bool IsMulticore() const; | 276 | bool IsMulticore() const; |
| 273 | 277 | ||
| 278 | bool IsShuttingDown() const; | ||
| 279 | |||
| 274 | void EnterSVCProfile(); | 280 | void EnterSVCProfile(); |
| 275 | 281 | ||
| 276 | void ExitSVCProfile(); | 282 | void ExitSVCProfile(); |
| @@ -326,6 +332,8 @@ public: | |||
| 326 | return slab_heap_container->transfer_memory; | 332 | return slab_heap_container->transfer_memory; |
| 327 | } else if constexpr (std::is_same_v<T, KWritableEvent>) { | 333 | } else if constexpr (std::is_same_v<T, KWritableEvent>) { |
| 328 | return slab_heap_container->writeable_event; | 334 | return slab_heap_container->writeable_event; |
| 335 | } else if constexpr (std::is_same_v<T, KCodeMemory>) { | ||
| 336 | return slab_heap_container->code_memory; | ||
| 329 | } | 337 | } |
| 330 | } | 338 | } |
| 331 | 339 | ||
| @@ -377,6 +385,7 @@ private: | |||
| 377 | KSlabHeap<KThread> thread; | 385 | KSlabHeap<KThread> thread; |
| 378 | KSlabHeap<KTransferMemory> transfer_memory; | 386 | KSlabHeap<KTransferMemory> transfer_memory; |
| 379 | KSlabHeap<KWritableEvent> writeable_event; | 387 | KSlabHeap<KWritableEvent> writeable_event; |
| 388 | KSlabHeap<KCodeMemory> code_memory; | ||
| 380 | }; | 389 | }; |
| 381 | 390 | ||
| 382 | std::unique_ptr<SlabHeapContainer> slab_heap_container; | 391 | std::unique_ptr<SlabHeapContainer> slab_heap_container; |
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 6721b6276..03f3dec10 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp | |||
| @@ -25,24 +25,27 @@ public: | |||
| 25 | void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); | 25 | void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); |
| 26 | 26 | ||
| 27 | private: | 27 | private: |
| 28 | std::vector<std::thread> threads; | 28 | std::vector<std::jthread> threads; |
| 29 | std::queue<std::function<void()>> requests; | 29 | std::queue<std::function<void()>> requests; |
| 30 | std::mutex queue_mutex; | 30 | std::mutex queue_mutex; |
| 31 | std::condition_variable condition; | 31 | std::condition_variable_any condition; |
| 32 | const std::string service_name; | 32 | const std::string service_name; |
| 33 | bool stop{}; | ||
| 34 | }; | 33 | }; |
| 35 | 34 | ||
| 36 | ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) | 35 | ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) |
| 37 | : service_name{name} { | 36 | : service_name{name} { |
| 38 | for (std::size_t i = 0; i < num_threads; ++i) | 37 | for (std::size_t i = 0; i < num_threads; ++i) { |
| 39 | threads.emplace_back([this, &kernel] { | 38 | threads.emplace_back([this, &kernel](std::stop_token stop_token) { |
| 40 | Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); | 39 | Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); |
| 41 | 40 | ||
| 42 | // Wait for first request before trying to acquire a render context | 41 | // Wait for first request before trying to acquire a render context |
| 43 | { | 42 | { |
| 44 | std::unique_lock lock{queue_mutex}; | 43 | std::unique_lock lock{queue_mutex}; |
| 45 | condition.wait(lock, [this] { return stop || !requests.empty(); }); | 44 | condition.wait(lock, stop_token, [this] { return !requests.empty(); }); |
| 45 | } | ||
| 46 | |||
| 47 | if (stop_token.stop_requested()) { | ||
| 48 | return; | ||
| 46 | } | 49 | } |
| 47 | 50 | ||
| 48 | kernel.RegisterHostThread(); | 51 | kernel.RegisterHostThread(); |
| @@ -52,10 +55,16 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std | |||
| 52 | 55 | ||
| 53 | { | 56 | { |
| 54 | std::unique_lock lock{queue_mutex}; | 57 | std::unique_lock lock{queue_mutex}; |
| 55 | condition.wait(lock, [this] { return stop || !requests.empty(); }); | 58 | condition.wait(lock, stop_token, [this] { return !requests.empty(); }); |
| 56 | if (stop || requests.empty()) { | 59 | |
| 60 | if (stop_token.stop_requested()) { | ||
| 57 | return; | 61 | return; |
| 58 | } | 62 | } |
| 63 | |||
| 64 | if (requests.empty()) { | ||
| 65 | continue; | ||
| 66 | } | ||
| 67 | |||
| 59 | task = std::move(requests.front()); | 68 | task = std::move(requests.front()); |
| 60 | requests.pop(); | 69 | requests.pop(); |
| 61 | } | 70 | } |
| @@ -63,6 +72,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std | |||
| 63 | task(); | 72 | task(); |
| 64 | } | 73 | } |
| 65 | }); | 74 | }); |
| 75 | } | ||
| 66 | } | 76 | } |
| 67 | 77 | ||
| 68 | void ServiceThread::Impl::QueueSyncRequest(KSession& session, | 78 | void ServiceThread::Impl::QueueSyncRequest(KSession& session, |
| @@ -88,12 +98,9 @@ void ServiceThread::Impl::QueueSyncRequest(KSession& session, | |||
| 88 | } | 98 | } |
| 89 | 99 | ||
| 90 | ServiceThread::Impl::~Impl() { | 100 | ServiceThread::Impl::~Impl() { |
| 91 | { | ||
| 92 | std::unique_lock lock{queue_mutex}; | ||
| 93 | stop = true; | ||
| 94 | } | ||
| 95 | condition.notify_all(); | 101 | condition.notify_all(); |
| 96 | for (std::thread& thread : threads) { | 102 | for (auto& thread : threads) { |
| 103 | thread.request_stop(); | ||
| 97 | thread.join(); | 104 | thread.join(); |
| 98 | } | 105 | } |
| 99 | } | 106 | } |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f9d99bc51..a9f7438ea 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| 19 | #include "core/hle/kernel/k_client_port.h" | 19 | #include "core/hle/kernel/k_client_port.h" |
| 20 | #include "core/hle/kernel/k_client_session.h" | 20 | #include "core/hle/kernel/k_client_session.h" |
| 21 | #include "core/hle/kernel/k_code_memory.h" | ||
| 21 | #include "core/hle/kernel/k_event.h" | 22 | #include "core/hle/kernel/k_event.h" |
| 22 | #include "core/hle/kernel/k_handle_table.h" | 23 | #include "core/hle/kernel/k_handle_table.h" |
| 23 | #include "core/hle/kernel/k_memory_block.h" | 24 | #include "core/hle/kernel/k_memory_block.h" |
| @@ -31,6 +32,7 @@ | |||
| 31 | #include "core/hle/kernel/k_shared_memory.h" | 32 | #include "core/hle/kernel/k_shared_memory.h" |
| 32 | #include "core/hle/kernel/k_synchronization_object.h" | 33 | #include "core/hle/kernel/k_synchronization_object.h" |
| 33 | #include "core/hle/kernel/k_thread.h" | 34 | #include "core/hle/kernel/k_thread.h" |
| 35 | #include "core/hle/kernel/k_thread_queue.h" | ||
| 34 | #include "core/hle/kernel/k_transfer_memory.h" | 36 | #include "core/hle/kernel/k_transfer_memory.h" |
| 35 | #include "core/hle/kernel/k_writable_event.h" | 37 | #include "core/hle/kernel/k_writable_event.h" |
| 36 | #include "core/hle/kernel/kernel.h" | 38 | #include "core/hle/kernel/kernel.h" |
| @@ -307,26 +309,29 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle, | |||
| 307 | 309 | ||
| 308 | /// Makes a blocking IPC call to an OS service. | 310 | /// Makes a blocking IPC call to an OS service. |
| 309 | static ResultCode SendSyncRequest(Core::System& system, Handle handle) { | 311 | static ResultCode SendSyncRequest(Core::System& system, Handle handle) { |
| 310 | |||
| 311 | auto& kernel = system.Kernel(); | 312 | auto& kernel = system.Kernel(); |
| 312 | 313 | ||
| 314 | // Create the wait queue. | ||
| 315 | KThreadQueue wait_queue(kernel); | ||
| 316 | |||
| 317 | // Get the client session from its handle. | ||
| 318 | KScopedAutoObject session = | ||
| 319 | kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); | ||
| 320 | R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | ||
| 321 | |||
| 322 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | ||
| 323 | |||
| 313 | auto thread = kernel.CurrentScheduler()->GetCurrentThread(); | 324 | auto thread = kernel.CurrentScheduler()->GetCurrentThread(); |
| 314 | { | 325 | { |
| 315 | KScopedSchedulerLock lock(kernel); | 326 | KScopedSchedulerLock lock(kernel); |
| 316 | thread->SetState(ThreadState::Waiting); | 327 | |
| 317 | thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); | 328 | // This is a synchronous request, so we should wait for our request to complete. |
| 318 | 329 | GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue)); | |
| 319 | { | 330 | GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); |
| 320 | KScopedAutoObject session = | 331 | session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming()); |
| 321 | kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); | ||
| 322 | R_UNLESS(session.IsNotNull(), ResultInvalidHandle); | ||
| 323 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | ||
| 324 | session->SendSyncRequest(thread, system.Memory(), system.CoreTiming()); | ||
| 325 | } | ||
| 326 | } | 332 | } |
| 327 | 333 | ||
| 328 | KSynchronizationObject* dummy{}; | 334 | return thread->GetWaitResult(); |
| 329 | return thread->GetWaitResult(std::addressof(dummy)); | ||
| 330 | } | 335 | } |
| 331 | 336 | ||
| 332 | static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { | 337 | static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { |
| @@ -873,7 +878,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle | |||
| 873 | const u64 thread_ticks = current_thread->GetCpuTime(); | 878 | const u64 thread_ticks = current_thread->GetCpuTime(); |
| 874 | 879 | ||
| 875 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); | 880 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); |
| 876 | } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { | 881 | } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { |
| 877 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; | 882 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; |
| 878 | } | 883 | } |
| 879 | 884 | ||
| @@ -887,7 +892,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle | |||
| 887 | return ResultInvalidHandle; | 892 | return ResultInvalidHandle; |
| 888 | } | 893 | } |
| 889 | 894 | ||
| 890 | if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id != system.CurrentCoreIndex()) { | 895 | if (info_sub_id != 0xFFFFFFFFFFFFFFFF && |
| 896 | info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) { | ||
| 891 | LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id); | 897 | LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id); |
| 892 | return ResultInvalidCombination; | 898 | return ResultInvalidCombination; |
| 893 | } | 899 | } |
| @@ -1169,6 +1175,8 @@ static u32 GetCurrentProcessorNumber32(Core::System& system) { | |||
| 1169 | return GetCurrentProcessorNumber(system); | 1175 | return GetCurrentProcessorNumber(system); |
| 1170 | } | 1176 | } |
| 1171 | 1177 | ||
| 1178 | namespace { | ||
| 1179 | |||
| 1172 | constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { | 1180 | constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { |
| 1173 | switch (perm) { | 1181 | switch (perm) { |
| 1174 | case Svc::MemoryPermission::Read: | 1182 | case Svc::MemoryPermission::Read: |
| @@ -1179,10 +1187,40 @@ constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { | |||
| 1179 | } | 1187 | } |
| 1180 | } | 1188 | } |
| 1181 | 1189 | ||
| 1182 | constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) { | 1190 | [[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) { |
| 1183 | return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare; | 1191 | return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare; |
| 1184 | } | 1192 | } |
| 1185 | 1193 | ||
| 1194 | constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1195 | switch (perm) { | ||
| 1196 | case Svc::MemoryPermission::None: | ||
| 1197 | case Svc::MemoryPermission::Read: | ||
| 1198 | case Svc::MemoryPermission::ReadWrite: | ||
| 1199 | case Svc::MemoryPermission::ReadExecute: | ||
| 1200 | return true; | ||
| 1201 | default: | ||
| 1202 | return false; | ||
| 1203 | } | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1207 | return perm == Svc::MemoryPermission::ReadWrite; | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1211 | return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute; | ||
| 1212 | } | ||
| 1213 | |||
| 1214 | constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1215 | return perm == Svc::MemoryPermission::None; | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1219 | return perm == Svc::MemoryPermission::None; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | } // Anonymous namespace | ||
| 1223 | |||
| 1186 | static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, | 1224 | static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, |
| 1187 | u64 size, Svc::MemoryPermission map_perm) { | 1225 | u64 size, Svc::MemoryPermission map_perm) { |
| 1188 | LOG_TRACE(Kernel_SVC, | 1226 | LOG_TRACE(Kernel_SVC, |
| @@ -1262,6 +1300,223 @@ static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle, | |||
| 1262 | return UnmapSharedMemory(system, shmem_handle, address, size); | 1300 | return UnmapSharedMemory(system, shmem_handle, address, size); |
| 1263 | } | 1301 | } |
| 1264 | 1302 | ||
| 1303 | static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle, | ||
| 1304 | VAddr address, u64 size, Svc::MemoryPermission perm) { | ||
| 1305 | LOG_TRACE(Kernel_SVC, | ||
| 1306 | "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | ||
| 1307 | process_handle, address, size, perm); | ||
| 1308 | |||
| 1309 | // Validate the address/size. | ||
| 1310 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1311 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1312 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1313 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1314 | |||
| 1315 | // Validate the memory permission. | ||
| 1316 | R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1317 | |||
| 1318 | // Get the process from its handle. | ||
| 1319 | KScopedAutoObject process = | ||
| 1320 | system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); | ||
| 1321 | R_UNLESS(process.IsNotNull(), ResultInvalidHandle); | ||
| 1322 | |||
| 1323 | // Validate that the address is in range. | ||
| 1324 | auto& page_table = process->PageTable(); | ||
| 1325 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 1326 | |||
| 1327 | // Set the memory permission. | ||
| 1328 | return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm)); | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 1332 | VAddr src_address, u64 size) { | ||
| 1333 | LOG_TRACE(Kernel_SVC, | ||
| 1334 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 1335 | dst_address, process_handle, src_address, size); | ||
| 1336 | |||
| 1337 | // Validate the address/size. | ||
| 1338 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 1339 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 1340 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1341 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1342 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 1343 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 1344 | |||
| 1345 | // Get the processes. | ||
| 1346 | KProcess* dst_process = system.CurrentProcess(); | ||
| 1347 | KScopedAutoObject src_process = | ||
| 1348 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 1349 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 1350 | |||
| 1351 | // Get the page tables. | ||
| 1352 | auto& dst_pt = dst_process->PageTable(); | ||
| 1353 | auto& src_pt = src_process->PageTable(); | ||
| 1354 | |||
| 1355 | // Validate that the mapping is in range. | ||
| 1356 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 1357 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 1358 | ResultInvalidMemoryRegion); | ||
| 1359 | |||
| 1360 | // Create a new page group. | ||
| 1361 | KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); | ||
| 1362 | KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); | ||
| 1363 | |||
| 1364 | // Map the group. | ||
| 1365 | R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, | ||
| 1366 | KMemoryPermission::UserReadWrite)); | ||
| 1367 | |||
| 1368 | return ResultSuccess; | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 1372 | VAddr src_address, u64 size) { | ||
| 1373 | LOG_TRACE(Kernel_SVC, | ||
| 1374 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 1375 | dst_address, process_handle, src_address, size); | ||
| 1376 | |||
| 1377 | // Validate the address/size. | ||
| 1378 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 1379 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 1380 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1381 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1382 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 1383 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 1384 | |||
| 1385 | // Get the processes. | ||
| 1386 | KProcess* dst_process = system.CurrentProcess(); | ||
| 1387 | KScopedAutoObject src_process = | ||
| 1388 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 1389 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 1390 | |||
| 1391 | // Get the page tables. | ||
| 1392 | auto& dst_pt = dst_process->PageTable(); | ||
| 1393 | auto& src_pt = src_process->PageTable(); | ||
| 1394 | |||
| 1395 | // Validate that the mapping is in range. | ||
| 1396 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 1397 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 1398 | ResultInvalidMemoryRegion); | ||
| 1399 | |||
| 1400 | // Unmap the memory. | ||
| 1401 | R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); | ||
| 1402 | |||
| 1403 | return ResultSuccess; | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { | ||
| 1407 | LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}", | ||
| 1408 | static_cast<void*>(out), address, size); | ||
| 1409 | // Get kernel instance. | ||
| 1410 | auto& kernel = system.Kernel(); | ||
| 1411 | |||
| 1412 | // Validate address / size. | ||
| 1413 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1414 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1415 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1416 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1417 | |||
| 1418 | // Create the code memory. | ||
| 1419 | |||
| 1420 | KCodeMemory* code_mem = KCodeMemory::Create(kernel); | ||
| 1421 | R_UNLESS(code_mem != nullptr, ResultOutOfResource); | ||
| 1422 | |||
| 1423 | // Verify that the region is in range. | ||
| 1424 | R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), | ||
| 1425 | ResultInvalidCurrentMemory); | ||
| 1426 | |||
| 1427 | // Initialize the code memory. | ||
| 1428 | R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); | ||
| 1429 | |||
| 1430 | // Register the code memory. | ||
| 1431 | KCodeMemory::Register(kernel, code_mem); | ||
| 1432 | |||
| 1433 | // Add the code memory to the handle table. | ||
| 1434 | R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); | ||
| 1435 | |||
| 1436 | code_mem->Close(); | ||
| 1437 | |||
| 1438 | return ResultSuccess; | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 1442 | VAddr address, size_t size, Svc::MemoryPermission perm) { | ||
| 1443 | |||
| 1444 | LOG_TRACE(Kernel_SVC, | ||
| 1445 | "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " | ||
| 1446 | "permission=0x{:X}", | ||
| 1447 | code_memory_handle, operation, address, size, perm); | ||
| 1448 | |||
| 1449 | // Validate the address / size. | ||
| 1450 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1451 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1452 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1453 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1454 | |||
| 1455 | // Get the code memory from its handle. | ||
| 1456 | KScopedAutoObject code_mem = | ||
| 1457 | system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle); | ||
| 1458 | R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); | ||
| 1459 | |||
| 1460 | // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. | ||
| 1461 | // This enables homebrew usage of these SVCs for JIT. | ||
| 1462 | |||
| 1463 | // Perform the operation. | ||
| 1464 | switch (static_cast<CodeMemoryOperation>(operation)) { | ||
| 1465 | case CodeMemoryOperation::Map: { | ||
| 1466 | // Check that the region is in range. | ||
| 1467 | R_UNLESS( | ||
| 1468 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 1469 | ResultInvalidMemoryRegion); | ||
| 1470 | |||
| 1471 | // Check the memory permission. | ||
| 1472 | R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1473 | |||
| 1474 | // Map the memory. | ||
| 1475 | R_TRY(code_mem->Map(address, size)); | ||
| 1476 | } break; | ||
| 1477 | case CodeMemoryOperation::Unmap: { | ||
| 1478 | // Check that the region is in range. | ||
| 1479 | R_UNLESS( | ||
| 1480 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 1481 | ResultInvalidMemoryRegion); | ||
| 1482 | |||
| 1483 | // Check the memory permission. | ||
| 1484 | R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1485 | |||
| 1486 | // Unmap the memory. | ||
| 1487 | R_TRY(code_mem->Unmap(address, size)); | ||
| 1488 | } break; | ||
| 1489 | case CodeMemoryOperation::MapToOwner: { | ||
| 1490 | // Check that the region is in range. | ||
| 1491 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 1492 | KMemoryState::GeneratedCode), | ||
| 1493 | ResultInvalidMemoryRegion); | ||
| 1494 | |||
| 1495 | // Check the memory permission. | ||
| 1496 | R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1497 | |||
| 1498 | // Map the memory to its owner. | ||
| 1499 | R_TRY(code_mem->MapToOwner(address, size, perm)); | ||
| 1500 | } break; | ||
| 1501 | case CodeMemoryOperation::UnmapFromOwner: { | ||
| 1502 | // Check that the region is in range. | ||
| 1503 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 1504 | KMemoryState::GeneratedCode), | ||
| 1505 | ResultInvalidMemoryRegion); | ||
| 1506 | |||
| 1507 | // Check the memory permission. | ||
| 1508 | R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1509 | |||
| 1510 | // Unmap the memory from its owner. | ||
| 1511 | R_TRY(code_mem->UnmapFromOwner(address, size)); | ||
| 1512 | } break; | ||
| 1513 | default: | ||
| 1514 | return ResultInvalidEnumValue; | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | return ResultSuccess; | ||
| 1518 | } | ||
| 1519 | |||
| 1265 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, | 1520 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, |
| 1266 | VAddr page_info_address, Handle process_handle, | 1521 | VAddr page_info_address, Handle process_handle, |
| 1267 | VAddr address) { | 1522 | VAddr address) { |
| @@ -1459,10 +1714,14 @@ static void ExitProcess32(Core::System& system) { | |||
| 1459 | ExitProcess(system); | 1714 | ExitProcess(system); |
| 1460 | } | 1715 | } |
| 1461 | 1716 | ||
| 1462 | static constexpr bool IsValidVirtualCoreId(int32_t core_id) { | 1717 | namespace { |
| 1718 | |||
| 1719 | constexpr bool IsValidVirtualCoreId(int32_t core_id) { | ||
| 1463 | return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); | 1720 | return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); |
| 1464 | } | 1721 | } |
| 1465 | 1722 | ||
| 1723 | } // Anonymous namespace | ||
| 1724 | |||
| 1466 | /// Creates a new thread | 1725 | /// Creates a new thread |
| 1467 | static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, | 1726 | static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, |
| 1468 | VAddr stack_bottom, u32 priority, s32 core_id) { | 1727 | VAddr stack_bottom, u32 priority, s32 core_id) { |
| @@ -1846,7 +2105,9 @@ static ResultCode ResetSignal32(Core::System& system, Handle handle) { | |||
| 1846 | return ResetSignal(system, handle); | 2105 | return ResetSignal(system, handle); |
| 1847 | } | 2106 | } |
| 1848 | 2107 | ||
| 1849 | static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { | 2108 | namespace { |
| 2109 | |||
| 2110 | constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { | ||
| 1850 | switch (perm) { | 2111 | switch (perm) { |
| 1851 | case MemoryPermission::None: | 2112 | case MemoryPermission::None: |
| 1852 | case MemoryPermission::Read: | 2113 | case MemoryPermission::Read: |
| @@ -1857,6 +2118,8 @@ static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { | |||
| 1857 | } | 2118 | } |
| 1858 | } | 2119 | } |
| 1859 | 2120 | ||
| 2121 | } // Anonymous namespace | ||
| 2122 | |||
| 1860 | /// Creates a TransferMemory object | 2123 | /// Creates a TransferMemory object |
| 1861 | static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, | 2124 | static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, |
| 1862 | MemoryPermission map_perm) { | 2125 | MemoryPermission map_perm) { |
| @@ -2548,8 +2811,8 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2548 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, | 2811 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, |
| 2549 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, | 2812 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, |
| 2550 | {0x4A, nullptr, "SetUnsafeLimit"}, | 2813 | {0x4A, nullptr, "SetUnsafeLimit"}, |
| 2551 | {0x4B, nullptr, "CreateCodeMemory"}, | 2814 | {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"}, |
| 2552 | {0x4C, nullptr, "ControlCodeMemory"}, | 2815 | {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"}, |
| 2553 | {0x4D, nullptr, "SleepSystem"}, | 2816 | {0x4D, nullptr, "SleepSystem"}, |
| 2554 | {0x4E, nullptr, "ReadWriteRegister"}, | 2817 | {0x4E, nullptr, "ReadWriteRegister"}, |
| 2555 | {0x4F, nullptr, "SetProcessActivity"}, | 2818 | {0x4F, nullptr, "SetProcessActivity"}, |
| @@ -2588,9 +2851,9 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2588 | {0x70, nullptr, "CreatePort"}, | 2851 | {0x70, nullptr, "CreatePort"}, |
| 2589 | {0x71, nullptr, "ManageNamedPort"}, | 2852 | {0x71, nullptr, "ManageNamedPort"}, |
| 2590 | {0x72, nullptr, "ConnectToPort"}, | 2853 | {0x72, nullptr, "ConnectToPort"}, |
| 2591 | {0x73, nullptr, "SetProcessMemoryPermission"}, | 2854 | {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"}, |
| 2592 | {0x74, nullptr, "MapProcessMemory"}, | 2855 | {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"}, |
| 2593 | {0x75, nullptr, "UnmapProcessMemory"}, | 2856 | {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"}, |
| 2594 | {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, | 2857 | {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, |
| 2595 | {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, | 2858 | {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, |
| 2596 | {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, | 2859 | {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 6e62e656f..86255fe6d 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -73,6 +73,23 @@ void SvcWrap64(Core::System& system) { | |||
| 73 | .raw); | 73 | .raw); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | // Used by MapProcessMemory and UnmapProcessMemory | ||
| 77 | template <ResultCode func(Core::System&, u64, u32, u64, u64)> | ||
| 78 | void SvcWrap64(Core::System& system) { | ||
| 79 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), | ||
| 80 | Param(system, 2), Param(system, 3)) | ||
| 81 | .raw); | ||
| 82 | } | ||
| 83 | |||
| 84 | // Used by ControlCodeMemory | ||
| 85 | template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> | ||
| 86 | void SvcWrap64(Core::System& system) { | ||
| 87 | FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), | ||
| 88 | static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3), | ||
| 89 | static_cast<Svc::MemoryPermission>(Param(system, 4))) | ||
| 90 | .raw); | ||
| 91 | } | ||
| 92 | |||
| 76 | template <ResultCode func(Core::System&, u32*)> | 93 | template <ResultCode func(Core::System&, u32*)> |
| 77 | void SvcWrap64(Core::System& system) { | 94 | void SvcWrap64(Core::System& system) { |
| 78 | u32 param = 0; | 95 | u32 param = 0; |
| @@ -301,6 +318,16 @@ void SvcWrap64(Core::System& system) { | |||
| 301 | FuncReturn(system, retval); | 318 | FuncReturn(system, retval); |
| 302 | } | 319 | } |
| 303 | 320 | ||
| 321 | // Used by CreateCodeMemory | ||
| 322 | template <ResultCode func(Core::System&, Handle*, u64, u64)> | ||
| 323 | void SvcWrap64(Core::System& system) { | ||
| 324 | u32 param_1 = 0; | ||
| 325 | const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2)).raw; | ||
| 326 | |||
| 327 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 328 | FuncReturn(system, retval); | ||
| 329 | } | ||
| 330 | |||
| 304 | template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> | 331 | template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> |
| 305 | void SvcWrap64(Core::System& system) { | 332 | void SvcWrap64(Core::System& system) { |
| 306 | u32 param_1 = 0; | 333 | u32 param_1 = 0; |
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 8cd7279a3..aa985d820 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/hle/kernel/k_scheduler.h" | ||
| 8 | #include "core/hle/kernel/k_thread.h" | 9 | #include "core/hle/kernel/k_thread.h" |
| 9 | #include "core/hle/kernel/time_manager.h" | 10 | #include "core/hle/kernel/time_manager.h" |
| 10 | 11 | ||
| @@ -15,7 +16,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { | |||
| 15 | Core::Timing::CreateEvent("Kernel::TimeManagerCallback", | 16 | Core::Timing::CreateEvent("Kernel::TimeManagerCallback", |
| 16 | [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { | 17 | [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { |
| 17 | KThread* thread = reinterpret_cast<KThread*>(thread_handle); | 18 | KThread* thread = reinterpret_cast<KThread*>(thread_handle); |
| 18 | thread->Wakeup(); | 19 | { |
| 20 | KScopedSchedulerLock sl(system.Kernel()); | ||
| 21 | thread->OnTimer(); | ||
| 22 | } | ||
| 19 | }); | 23 | }); |
| 20 | } | 24 | } |
| 21 | 25 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index aee8d4f93..e60661fe1 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | #include "core/hle/service/apm/apm_controller.h" | 30 | #include "core/hle/service/apm/apm_controller.h" |
| 31 | #include "core/hle/service/apm/apm_interface.h" | 31 | #include "core/hle/service/apm/apm_interface.h" |
| 32 | #include "core/hle/service/bcat/backend/backend.h" | 32 | #include "core/hle/service/bcat/backend/backend.h" |
| 33 | #include "core/hle/service/caps/caps.h" | ||
| 33 | #include "core/hle/service/filesystem/filesystem.h" | 34 | #include "core/hle/service/filesystem/filesystem.h" |
| 34 | #include "core/hle/service/ns/ns.h" | 35 | #include "core/hle/service/ns/ns.h" |
| 35 | #include "core/hle/service/nvflinger/nvflinger.h" | 36 | #include "core/hle/service/nvflinger/nvflinger.h" |
| @@ -298,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv | |||
| 298 | {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, | 299 | {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, |
| 299 | {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, | 300 | {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, |
| 300 | {110, nullptr, "SetApplicationAlbumUserData"}, | 301 | {110, nullptr, "SetApplicationAlbumUserData"}, |
| 301 | {120, nullptr, "SaveCurrentScreenshot"}, | 302 | {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, |
| 302 | {130, nullptr, "SetRecordVolumeMuted"}, | 303 | {130, nullptr, "SetRecordVolumeMuted"}, |
| 303 | {1000, nullptr, "GetDebugStorageChannel"}, | 304 | {1000, nullptr, "GetDebugStorageChannel"}, |
| 304 | }; | 305 | }; |
| @@ -579,6 +580,17 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestCo | |||
| 579 | rb.Push(ResultSuccess); | 580 | rb.Push(ResultSuccess); |
| 580 | } | 581 | } |
| 581 | 582 | ||
| 583 | void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) { | ||
| 584 | IPC::RequestParser rp{ctx}; | ||
| 585 | |||
| 586 | const auto album_report_option = rp.PopEnum<Capture::AlbumReportOption>(); | ||
| 587 | |||
| 588 | LOG_WARNING(Service_AM, "(STUBBED) called. album_report_option={}", album_report_option); | ||
| 589 | |||
| 590 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 591 | rb.Push(ResultSuccess); | ||
| 592 | } | ||
| 593 | |||
| 582 | AppletMessageQueue::AppletMessageQueue(Core::System& system) | 594 | AppletMessageQueue::AppletMessageQueue(Core::System& system) |
| 583 | : service_context{system, "AppletMessageQueue"} { | 595 | : service_context{system, "AppletMessageQueue"} { |
| 584 | on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); | 596 | on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 202d20757..2a578aea5 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -151,6 +151,7 @@ private: | |||
| 151 | void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); | 151 | void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); |
| 152 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); | 152 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); |
| 153 | void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); | 153 | void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); |
| 154 | void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx); | ||
| 154 | 155 | ||
| 155 | enum class ScreenshotPermission : u32 { | 156 | enum class ScreenshotPermission : u32 { |
| 156 | Inherit = 0, | 157 | Inherit = 0, |
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp index 2721679c1..d073f2210 100644 --- a/src/core/hle/service/am/applets/applet_controller.cpp +++ b/src/core/hle/service/am/applets/applet_controller.cpp | |||
| @@ -10,6 +10,9 @@ | |||
| 10 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/frontend/applets/controller.h" | 12 | #include "core/frontend/applets/controller.h" |
| 13 | #include "core/hid/emulated_controller.h" | ||
| 14 | #include "core/hid/hid_core.h" | ||
| 15 | #include "core/hid/hid_types.h" | ||
| 13 | #include "core/hle/result.h" | 16 | #include "core/hle/result.h" |
| 14 | #include "core/hle/service/am/am.h" | 17 | #include "core/hle/service/am/am.h" |
| 15 | #include "core/hle/service/am/applets/applet_controller.h" | 18 | #include "core/hle/service/am/applets/applet_controller.h" |
| @@ -25,7 +28,7 @@ namespace Service::AM::Applets { | |||
| 25 | static Core::Frontend::ControllerParameters ConvertToFrontendParameters( | 28 | static Core::Frontend::ControllerParameters ConvertToFrontendParameters( |
| 26 | ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, | 29 | ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, |
| 27 | std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { | 30 | std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { |
| 28 | HID::Controller_NPad::NpadStyleSet npad_style_set; | 31 | Core::HID::NpadStyleTag npad_style_set; |
| 29 | npad_style_set.raw = private_arg.style_set; | 32 | npad_style_set.raw = private_arg.style_set; |
| 30 | 33 | ||
| 31 | return { | 34 | return { |
| @@ -243,19 +246,11 @@ void Controller::Execute() { | |||
| 243 | void Controller::ConfigurationComplete() { | 246 | void Controller::ConfigurationComplete() { |
| 244 | ControllerSupportResultInfo result_info{}; | 247 | ControllerSupportResultInfo result_info{}; |
| 245 | 248 | ||
| 246 | const auto& players = Settings::values.players.GetValue(); | ||
| 247 | |||
| 248 | // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. | 249 | // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. |
| 249 | // Otherwise, only count connected players from P1-P8. | 250 | // Otherwise, only count connected players from P1-P8. |
| 250 | result_info.player_count = | 251 | result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount(); |
| 251 | is_single_mode | 252 | |
| 252 | ? 1 | 253 | result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId()); |
| 253 | : static_cast<s8>(std::count_if(players.begin(), players.end() - 2, | ||
| 254 | [](const auto& player) { return player.connected; })); | ||
| 255 | |||
| 256 | result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance( | ||
| 257 | players.begin(), std::find_if(players.begin(), players.end(), | ||
| 258 | [](const auto& player) { return player.connected; }))); | ||
| 259 | 254 | ||
| 260 | result_info.result = 0; | 255 | result_info.result = 0; |
| 261 | 256 | ||
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h index 0a34c4fc0..1a832505e 100644 --- a/src/core/hle/service/am/applets/applet_controller.h +++ b/src/core/hle/service/am/applets/applet_controller.h | |||
| @@ -16,6 +16,10 @@ namespace Core { | |||
| 16 | class System; | 16 | class System; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace Core::HID { | ||
| 20 | enum class NpadStyleSet : u32; | ||
| 21 | } | ||
| 22 | |||
| 19 | namespace Service::AM::Applets { | 23 | namespace Service::AM::Applets { |
| 20 | 24 | ||
| 21 | using IdentificationColor = std::array<u8, 4>; | 25 | using IdentificationColor = std::array<u8, 4>; |
| @@ -52,7 +56,7 @@ struct ControllerSupportArgPrivate { | |||
| 52 | bool flag_1{}; | 56 | bool flag_1{}; |
| 53 | ControllerSupportMode mode{}; | 57 | ControllerSupportMode mode{}; |
| 54 | ControllerSupportCaller caller{}; | 58 | ControllerSupportCaller caller{}; |
| 55 | u32 style_set{}; | 59 | Core::HID::NpadStyleSet style_set{}; |
| 56 | u32 joy_hold_type{}; | 60 | u32 joy_hold_type{}; |
| 57 | }; | 61 | }; |
| 58 | static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, | 62 | static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, |
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 7320b1c0f..134ac1ee2 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -231,7 +231,7 @@ void AppletManager::SetDefaultAppletFrontendSet() { | |||
| 231 | void AppletManager::SetDefaultAppletsIfMissing() { | 231 | void AppletManager::SetDefaultAppletsIfMissing() { |
| 232 | if (frontend.controller == nullptr) { | 232 | if (frontend.controller == nullptr) { |
| 233 | frontend.controller = | 233 | frontend.controller = |
| 234 | std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager()); | 234 | std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore()); |
| 235 | } | 235 | } |
| 236 | 236 | ||
| 237 | if (frontend.error == nullptr) { | 237 | if (frontend.error == nullptr) { |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 7da1f2969..981b6c996 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -96,7 +96,7 @@ private: | |||
| 96 | 96 | ||
| 97 | bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, | 97 | bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, |
| 98 | std::vector<opus_int16>& output, u64* out_performance_time) const { | 98 | std::vector<opus_int16>& output, u64* out_performance_time) const { |
| 99 | const auto start_time = std::chrono::high_resolution_clock::now(); | 99 | const auto start_time = std::chrono::steady_clock::now(); |
| 100 | const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); | 100 | const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); |
| 101 | if (sizeof(OpusPacketHeader) > input.size()) { | 101 | if (sizeof(OpusPacketHeader) > input.size()) { |
| 102 | LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", | 102 | LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", |
| @@ -135,7 +135,7 @@ private: | |||
| 135 | return false; | 135 | return false; |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | const auto end_time = std::chrono::high_resolution_clock::now() - start_time; | 138 | const auto end_time = std::chrono::steady_clock::now() - start_time; |
| 139 | sample_count = out_sample_count; | 139 | sample_count = out_sample_count; |
| 140 | consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size); | 140 | consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size); |
| 141 | if (out_performance_time != nullptr) { | 141 | if (out_performance_time != nullptr) { |
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index b18adcb9d..7254055e6 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h | |||
| @@ -24,7 +24,7 @@ enum class AlbumImageOrientation { | |||
| 24 | Orientation3 = 3, | 24 | Orientation3 = 3, |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | enum class AlbumReportOption { | 27 | enum class AlbumReportOption : s32 { |
| 28 | Disable = 0, | 28 | Disable = 0, |
| 29 | Enable = 1, | 29 | Enable = 1, |
| 30 | }; | 30 | }; |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 68c9240ae..3c36f4085 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -17,10 +17,11 @@ namespace Service::Friend { | |||
| 17 | 17 | ||
| 18 | class IFriendService final : public ServiceFramework<IFriendService> { | 18 | class IFriendService final : public ServiceFramework<IFriendService> { |
| 19 | public: | 19 | public: |
| 20 | explicit IFriendService(Core::System& system_) : ServiceFramework{system_, "IFriendService"} { | 20 | explicit IFriendService(Core::System& system_) |
| 21 | : ServiceFramework{system_, "IFriendService"}, service_context{system, "IFriendService"} { | ||
| 21 | // clang-format off | 22 | // clang-format off |
| 22 | static const FunctionInfo functions[] = { | 23 | static const FunctionInfo functions[] = { |
| 23 | {0, nullptr, "GetCompletionEvent"}, | 24 | {0, &IFriendService::GetCompletionEvent, "GetCompletionEvent"}, |
| 24 | {1, nullptr, "Cancel"}, | 25 | {1, nullptr, "Cancel"}, |
| 25 | {10100, nullptr, "GetFriendListIds"}, | 26 | {10100, nullptr, "GetFriendListIds"}, |
| 26 | {10101, &IFriendService::GetFriendList, "GetFriendList"}, | 27 | {10101, &IFriendService::GetFriendList, "GetFriendList"}, |
| @@ -109,6 +110,12 @@ public: | |||
| 109 | // clang-format on | 110 | // clang-format on |
| 110 | 111 | ||
| 111 | RegisterHandlers(functions); | 112 | RegisterHandlers(functions); |
| 113 | |||
| 114 | completion_event = service_context.CreateEvent("IFriendService:CompletionEvent"); | ||
| 115 | } | ||
| 116 | |||
| 117 | ~IFriendService() override { | ||
| 118 | service_context.CloseEvent(completion_event); | ||
| 112 | } | 119 | } |
| 113 | 120 | ||
| 114 | private: | 121 | private: |
| @@ -129,6 +136,14 @@ private: | |||
| 129 | }; | 136 | }; |
| 130 | static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); | 137 | static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); |
| 131 | 138 | ||
| 139 | void GetCompletionEvent(Kernel::HLERequestContext& ctx) { | ||
| 140 | LOG_DEBUG(Service_Friend, "called"); | ||
| 141 | |||
| 142 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 143 | rb.Push(ResultSuccess); | ||
| 144 | rb.PushCopyObjects(completion_event->GetReadableEvent()); | ||
| 145 | } | ||
| 146 | |||
| 132 | void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) { | 147 | void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) { |
| 133 | // This is safe to stub, as there should be no adverse consequences from reporting no | 148 | // This is safe to stub, as there should be no adverse consequences from reporting no |
| 134 | // blocked users. | 149 | // blocked users. |
| @@ -179,6 +194,10 @@ private: | |||
| 179 | rb.Push<u32>(0); // Friend count | 194 | rb.Push<u32>(0); // Friend count |
| 180 | // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" | 195 | // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" |
| 181 | } | 196 | } |
| 197 | |||
| 198 | KernelHelpers::ServiceContext service_context; | ||
| 199 | |||
| 200 | Kernel::KEvent* completion_event; | ||
| 182 | }; | 201 | }; |
| 183 | 202 | ||
| 184 | class INotificationService final : public ServiceFramework<INotificationService> { | 203 | class INotificationService final : public ServiceFramework<INotificationService> { |
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index a08dc9758..b24d469cf 100644 --- a/src/core/hle/service/glue/glue.cpp +++ b/src/core/hle/service/glue/glue.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "core/hle/service/glue/bgtc.h" | 8 | #include "core/hle/service/glue/bgtc.h" |
| 9 | #include "core/hle/service/glue/ectx.h" | 9 | #include "core/hle/service/glue/ectx.h" |
| 10 | #include "core/hle/service/glue/glue.h" | 10 | #include "core/hle/service/glue/glue.h" |
| 11 | #include "core/hle/service/glue/notif.h" | ||
| 11 | 12 | ||
| 12 | namespace Service::Glue { | 13 | namespace Service::Glue { |
| 13 | 14 | ||
| @@ -24,6 +25,9 @@ void InstallInterfaces(Core::System& system) { | |||
| 24 | 25 | ||
| 25 | // Error Context | 26 | // Error Context |
| 26 | std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager()); | 27 | std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager()); |
| 28 | |||
| 29 | // Notification Services for application | ||
| 30 | std::make_shared<NOTIF_A>(system)->InstallAsService(system.ServiceManager()); | ||
| 27 | } | 31 | } |
| 28 | 32 | ||
| 29 | } // namespace Service::Glue | 33 | } // namespace Service::Glue |
diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp new file mode 100644 index 000000000..c559ec9df --- /dev/null +++ b/src/core/hle/service/glue/notif.cpp | |||
| @@ -0,0 +1,44 @@ | |||
| 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/hle/ipc_helpers.h" | ||
| 6 | #include "core/hle/service/glue/notif.h" | ||
| 7 | |||
| 8 | namespace Service::Glue { | ||
| 9 | |||
| 10 | NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} { | ||
| 11 | // clang-format off | ||
| 12 | static const FunctionInfo functions[] = { | ||
| 13 | {500, nullptr, "RegisterAlarmSetting"}, | ||
| 14 | {510, nullptr, "UpdateAlarmSetting"}, | ||
| 15 | {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"}, | ||
| 16 | {530, nullptr, "LoadApplicationParameter"}, | ||
| 17 | {540, nullptr, "DeleteAlarmSetting"}, | ||
| 18 | {1000, &NOTIF_A::Initialize, "Initialize"}, | ||
| 19 | }; | ||
| 20 | // clang-format on | ||
| 21 | |||
| 22 | RegisterHandlers(functions); | ||
| 23 | } | ||
| 24 | |||
| 25 | NOTIF_A::~NOTIF_A() = default; | ||
| 26 | |||
| 27 | void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) { | ||
| 28 | // Returns an array of AlarmSetting | ||
| 29 | constexpr s32 alarm_count = 0; | ||
| 30 | |||
| 31 | LOG_WARNING(Service_NOTIF, "(STUBBED) called"); | ||
| 32 | |||
| 33 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 34 | rb.Push(ResultSuccess); | ||
| 35 | rb.Push(alarm_count); | ||
| 36 | } | ||
| 37 | |||
| 38 | void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) { | ||
| 39 | LOG_WARNING(Service_NOTIF, "(STUBBED) called"); | ||
| 40 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 41 | rb.Push(ResultSuccess); | ||
| 42 | } | ||
| 43 | |||
| 44 | } // namespace Service::Glue | ||
diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h new file mode 100644 index 000000000..6ecf2015c --- /dev/null +++ b/src/core/hle/service/glue/notif.h | |||
| @@ -0,0 +1,25 @@ | |||
| 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 "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::Glue { | ||
| 14 | |||
| 15 | class NOTIF_A final : public ServiceFramework<NOTIF_A> { | ||
| 16 | public: | ||
| 17 | explicit NOTIF_A(Core::System& system_); | ||
| 18 | ~NOTIF_A() override; | ||
| 19 | |||
| 20 | private: | ||
| 21 | void ListAlarmSettings(Kernel::HLERequestContext& ctx); | ||
| 22 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 23 | }; | ||
| 24 | |||
| 25 | } // namespace Service::Glue | ||
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp index bda6e2557..f0f3105dc 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp +++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp | |||
| @@ -4,13 +4,18 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/settings.h" | 5 | #include "common/settings.h" |
| 6 | #include "core/core_timing.h" | 6 | #include "core/core_timing.h" |
| 7 | #include "core/hid/emulated_console.h" | ||
| 8 | #include "core/hid/hid_core.h" | ||
| 7 | #include "core/hle/service/hid/controllers/console_sixaxis.h" | 9 | #include "core/hle/service/hid/controllers/console_sixaxis.h" |
| 8 | 10 | ||
| 9 | namespace Service::HID { | 11 | namespace Service::HID { |
| 10 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; | 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; |
| 11 | 13 | ||
| 12 | Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_) | 14 | Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_) |
| 13 | : ControllerBase{system_} {} | 15 | : ControllerBase{hid_core_} { |
| 16 | console = hid_core.GetEmulatedConsole(); | ||
| 17 | } | ||
| 18 | |||
| 14 | Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; | 19 | Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; |
| 15 | 20 | ||
| 16 | void Controller_ConsoleSixAxis::OnInit() {} | 21 | void Controller_ConsoleSixAxis::OnInit() {} |
| @@ -19,44 +24,31 @@ void Controller_ConsoleSixAxis::OnRelease() {} | |||
| 19 | 24 | ||
| 20 | void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 25 | void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 26 | std::size_t size) { |
| 22 | seven_six_axis.header.timestamp = core_timing.GetCPUTicks(); | ||
| 23 | seven_six_axis.header.total_entry_count = 17; | ||
| 24 | |||
| 25 | if (!IsControllerActivated() || !is_transfer_memory_set) { | 27 | if (!IsControllerActivated() || !is_transfer_memory_set) { |
| 26 | seven_six_axis.header.entry_count = 0; | 28 | seven_sixaxis_lifo.buffer_count = 0; |
| 27 | seven_six_axis.header.last_entry_index = 0; | 29 | seven_sixaxis_lifo.buffer_tail = 0; |
| 28 | return; | 30 | return; |
| 29 | } | 31 | } |
| 30 | seven_six_axis.header.entry_count = 16; | ||
| 31 | |||
| 32 | const auto& last_entry = | ||
| 33 | seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index]; | ||
| 34 | seven_six_axis.header.last_entry_index = (seven_six_axis.header.last_entry_index + 1) % 17; | ||
| 35 | auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index]; | ||
| 36 | 32 | ||
| 37 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 33 | const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state; |
| 38 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 34 | next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1; |
| 39 | 35 | ||
| 40 | // Try to read sixaxis sensor states | 36 | // Try to read sixaxis sensor states |
| 41 | MotionDevice motion_device{}; | 37 | const auto motion_status = console->GetMotion(); |
| 42 | const auto& device = motions[0]; | 38 | |
| 43 | if (device) { | 39 | console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; |
| 44 | std::tie(motion_device.accel, motion_device.gyro, motion_device.rotation, | ||
| 45 | motion_device.orientation, motion_device.quaternion) = device->GetStatus(); | ||
| 46 | console_six_axis.is_seven_six_axis_sensor_at_rest = motion_device.gyro.Length2() < 0.0001f; | ||
| 47 | } | ||
| 48 | 40 | ||
| 49 | cur_entry.accel = motion_device.accel; | 41 | next_seven_sixaxis_state.accel = motion_status.accel; |
| 50 | // Zero gyro values as they just mess up with the camera | 42 | // Zero gyro values as they just mess up with the camera |
| 51 | // Note: Probably a correct sensivity setting must be set | 43 | // Note: Probably a correct sensivity setting must be set |
| 52 | cur_entry.gyro = {}; | 44 | next_seven_sixaxis_state.gyro = {}; |
| 53 | cur_entry.quaternion = { | 45 | next_seven_sixaxis_state.quaternion = { |
| 54 | { | 46 | { |
| 55 | motion_device.quaternion.xyz.y, | 47 | motion_status.quaternion.xyz.y, |
| 56 | motion_device.quaternion.xyz.x, | 48 | motion_status.quaternion.xyz.x, |
| 57 | -motion_device.quaternion.w, | 49 | -motion_status.quaternion.w, |
| 58 | }, | 50 | }, |
| 59 | -motion_device.quaternion.xyz.z, | 51 | -motion_status.quaternion.xyz.z, |
| 60 | }; | 52 | }; |
| 61 | 53 | ||
| 62 | console_six_axis.sampling_number++; | 54 | console_six_axis.sampling_number++; |
| @@ -67,14 +59,8 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti | |||
| 67 | // Update console six axis shared memory | 59 | // Update console six axis shared memory |
| 68 | std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis)); | 60 | std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis)); |
| 69 | // Update seven six axis transfer memory | 61 | // Update seven six axis transfer memory |
| 70 | std::memcpy(transfer_memory, &seven_six_axis, sizeof(seven_six_axis)); | 62 | seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); |
| 71 | } | 63 | std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo)); |
| 72 | |||
| 73 | void Controller_ConsoleSixAxis::OnLoadInputDevices() { | ||
| 74 | const auto player = Settings::values.players.GetValue()[0]; | ||
| 75 | std::transform(player.motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, | ||
| 76 | player.motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions.begin(), | ||
| 77 | Input::CreateDevice<Input::MotionDevice>); | ||
| 78 | } | 64 | } |
| 79 | 65 | ||
| 80 | void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) { | 66 | void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) { |
| @@ -83,8 +69,7 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) { | |||
| 83 | } | 69 | } |
| 84 | 70 | ||
| 85 | void Controller_ConsoleSixAxis::ResetTimestamp() { | 71 | void Controller_ConsoleSixAxis::ResetTimestamp() { |
| 86 | auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index]; | 72 | seven_sixaxis_lifo.buffer_count = 0; |
| 87 | cur_entry.sampling_number = 0; | 73 | seven_sixaxis_lifo.buffer_tail = 0; |
| 88 | cur_entry.sampling_number2 = 0; | ||
| 89 | } | 74 | } |
| 90 | } // namespace Service::HID | 75 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h index fd8a427af..279241858 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.h +++ b/src/core/hle/service/hid/controllers/console_sixaxis.h | |||
| @@ -5,16 +5,21 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/bit_field.h" | 8 | |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/quaternion.h" | 10 | #include "common/quaternion.h" |
| 11 | #include "core/frontend/input.h" | 11 | #include "core/hid/hid_types.h" |
| 12 | #include "core/hle/service/hid/controllers/controller_base.h" | 12 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 13 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 14 | |||
| 15 | namespace Core::HID { | ||
| 16 | class EmulatedConsole; | ||
| 17 | } // namespace Core::HID | ||
| 13 | 18 | ||
| 14 | namespace Service::HID { | 19 | namespace Service::HID { |
| 15 | class Controller_ConsoleSixAxis final : public ControllerBase { | 20 | class Controller_ConsoleSixAxis final : public ControllerBase { |
| 16 | public: | 21 | public: |
| 17 | explicit Controller_ConsoleSixAxis(Core::System& system_); | 22 | explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_); |
| 18 | ~Controller_ConsoleSixAxis() override; | 23 | ~Controller_ConsoleSixAxis() override; |
| 19 | 24 | ||
| 20 | // Called when the controller is initialized | 25 | // Called when the controller is initialized |
| @@ -26,9 +31,6 @@ public: | |||
| 26 | // When the controller is requesting an update for the shared memory | 31 | // When the controller is requesting an update for the shared memory |
| 27 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; | 32 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; |
| 28 | 33 | ||
| 29 | // Called when input devices should be loaded | ||
| 30 | void OnLoadInputDevices() override; | ||
| 31 | |||
| 32 | // Called on InitializeSevenSixAxisSensor | 34 | // Called on InitializeSevenSixAxisSensor |
| 33 | void SetTransferMemoryPointer(u8* t_mem); | 35 | void SetTransferMemoryPointer(u8* t_mem); |
| 34 | 36 | ||
| @@ -38,43 +40,31 @@ public: | |||
| 38 | private: | 40 | private: |
| 39 | struct SevenSixAxisState { | 41 | struct SevenSixAxisState { |
| 40 | INSERT_PADDING_WORDS(4); // unused | 42 | INSERT_PADDING_WORDS(4); // unused |
| 41 | s64_le sampling_number{}; | 43 | s64 sampling_number{}; |
| 42 | s64_le sampling_number2{}; | ||
| 43 | u64 unknown{}; | 44 | u64 unknown{}; |
| 44 | Common::Vec3f accel{}; | 45 | Common::Vec3f accel{}; |
| 45 | Common::Vec3f gyro{}; | 46 | Common::Vec3f gyro{}; |
| 46 | Common::Quaternion<f32> quaternion{}; | 47 | Common::Quaternion<f32> quaternion{}; |
| 47 | }; | 48 | }; |
| 48 | static_assert(sizeof(SevenSixAxisState) == 0x50, "SevenSixAxisState is an invalid size"); | 49 | static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); |
| 49 | |||
| 50 | struct SevenSixAxisMemory { | ||
| 51 | CommonHeader header{}; | ||
| 52 | std::array<SevenSixAxisState, 0x21> sevensixaxis_states{}; | ||
| 53 | }; | ||
| 54 | static_assert(sizeof(SevenSixAxisMemory) == 0xA70, "SevenSixAxisMemory is an invalid size"); | ||
| 55 | 50 | ||
| 51 | // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat | ||
| 56 | struct ConsoleSharedMemory { | 52 | struct ConsoleSharedMemory { |
| 57 | u64_le sampling_number{}; | 53 | u64 sampling_number{}; |
| 58 | bool is_seven_six_axis_sensor_at_rest{}; | 54 | bool is_seven_six_axis_sensor_at_rest{}; |
| 55 | INSERT_PADDING_BYTES(4); // padding | ||
| 59 | f32 verticalization_error{}; | 56 | f32 verticalization_error{}; |
| 60 | Common::Vec3f gyro_bias{}; | 57 | Common::Vec3f gyro_bias{}; |
| 61 | }; | 58 | }; |
| 62 | static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); | 59 | static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); |
| 63 | 60 | ||
| 64 | struct MotionDevice { | 61 | Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; |
| 65 | Common::Vec3f accel; | 62 | static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); |
| 66 | Common::Vec3f gyro; | ||
| 67 | Common::Vec3f rotation; | ||
| 68 | std::array<Common::Vec3f, 3> orientation; | ||
| 69 | Common::Quaternion<f32> quaternion; | ||
| 70 | }; | ||
| 71 | 63 | ||
| 72 | using MotionArray = | 64 | Core::HID::EmulatedConsole* console; |
| 73 | std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>; | ||
| 74 | MotionArray motions; | ||
| 75 | u8* transfer_memory = nullptr; | 65 | u8* transfer_memory = nullptr; |
| 76 | bool is_transfer_memory_set = false; | 66 | bool is_transfer_memory_set = false; |
| 77 | ConsoleSharedMemory console_six_axis{}; | 67 | ConsoleSharedMemory console_six_axis{}; |
| 78 | SevenSixAxisMemory seven_six_axis{}; | 68 | SevenSixAxisState next_seven_sixaxis_state{}; |
| 79 | }; | 69 | }; |
| 80 | } // namespace Service::HID | 70 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp index 9d1e6db6a..788ae9ae7 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp | |||
| @@ -6,12 +6,12 @@ | |||
| 6 | 6 | ||
| 7 | namespace Service::HID { | 7 | namespace Service::HID { |
| 8 | 8 | ||
| 9 | ControllerBase::ControllerBase(Core::System& system_) : system(system_) {} | 9 | ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} |
| 10 | ControllerBase::~ControllerBase() = default; | 10 | ControllerBase::~ControllerBase() = default; |
| 11 | 11 | ||
| 12 | void ControllerBase::ActivateController() { | 12 | void ControllerBase::ActivateController() { |
| 13 | if (is_activated) { | 13 | if (is_activated) { |
| 14 | OnRelease(); | 14 | return; |
| 15 | } | 15 | } |
| 16 | is_activated = true; | 16 | is_activated = true; |
| 17 | OnInit(); | 17 | OnInit(); |
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index 1556fb08e..7450eb20a 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h | |||
| @@ -11,14 +11,14 @@ namespace Core::Timing { | |||
| 11 | class CoreTiming; | 11 | class CoreTiming; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | namespace Core { | 14 | namespace Core::HID { |
| 15 | class System; | 15 | class HIDCore; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace Service::HID { | 18 | namespace Service::HID { |
| 19 | class ControllerBase { | 19 | class ControllerBase { |
| 20 | public: | 20 | public: |
| 21 | explicit ControllerBase(Core::System& system_); | 21 | explicit ControllerBase(Core::HID::HIDCore& hid_core_); |
| 22 | virtual ~ControllerBase(); | 22 | virtual ~ControllerBase(); |
| 23 | 23 | ||
| 24 | // Called when the controller is initialized | 24 | // Called when the controller is initialized |
| @@ -35,26 +35,17 @@ public: | |||
| 35 | virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 35 | virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 36 | std::size_t size) {} | 36 | std::size_t size) {} |
| 37 | 37 | ||
| 38 | // Called when input devices should be loaded | ||
| 39 | virtual void OnLoadInputDevices() = 0; | ||
| 40 | |||
| 41 | void ActivateController(); | 38 | void ActivateController(); |
| 42 | 39 | ||
| 43 | void DeactivateController(); | 40 | void DeactivateController(); |
| 44 | 41 | ||
| 45 | bool IsControllerActivated() const; | 42 | bool IsControllerActivated() const; |
| 46 | 43 | ||
| 44 | static const std::size_t hid_entry_count = 17; | ||
| 45 | |||
| 47 | protected: | 46 | protected: |
| 48 | bool is_activated{false}; | 47 | bool is_activated{false}; |
| 49 | 48 | ||
| 50 | struct CommonHeader { | 49 | Core::HID::HIDCore& hid_core; |
| 51 | s64_le timestamp; | ||
| 52 | s64_le total_entry_count; | ||
| 53 | s64_le last_entry_index; | ||
| 54 | s64_le entry_count; | ||
| 55 | }; | ||
| 56 | static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); | ||
| 57 | |||
| 58 | Core::System& system; | ||
| 59 | }; | 50 | }; |
| 60 | } // namespace Service::HID | 51 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index d439b8fb0..6a6fb9cab 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -6,15 +6,19 @@ | |||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/settings.h" | 7 | #include "common/settings.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/hid/emulated_controller.h" | ||
| 10 | #include "core/hid/hid_core.h" | ||
| 11 | #include "core/hid/hid_types.h" | ||
| 9 | #include "core/hle/service/hid/controllers/debug_pad.h" | 12 | #include "core/hle/service/hid/controllers/debug_pad.h" |
| 10 | 13 | ||
| 11 | namespace Service::HID { | 14 | namespace Service::HID { |
| 15 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; | ||
| 12 | 16 | ||
| 13 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | 17 | Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_) |
| 14 | [[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | 18 | : ControllerBase{hid_core_} { |
| 15 | enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; | 19 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); |
| 20 | } | ||
| 16 | 21 | ||
| 17 | Controller_DebugPad::Controller_DebugPad(Core::System& system_) : ControllerBase{system_} {} | ||
| 18 | Controller_DebugPad::~Controller_DebugPad() = default; | 22 | Controller_DebugPad::~Controller_DebugPad() = default; |
| 19 | 23 | ||
| 20 | void Controller_DebugPad::OnInit() {} | 24 | void Controller_DebugPad::OnInit() {} |
| @@ -23,63 +27,29 @@ void Controller_DebugPad::OnRelease() {} | |||
| 23 | 27 | ||
| 24 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 28 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 25 | std::size_t size) { | 29 | std::size_t size) { |
| 26 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); | ||
| 27 | shared_memory.header.total_entry_count = 17; | ||
| 28 | |||
| 29 | if (!IsControllerActivated()) { | 30 | if (!IsControllerActivated()) { |
| 30 | shared_memory.header.entry_count = 0; | 31 | debug_pad_lifo.buffer_count = 0; |
| 31 | shared_memory.header.last_entry_index = 0; | 32 | debug_pad_lifo.buffer_tail = 0; |
| 33 | std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo)); | ||
| 32 | return; | 34 | return; |
| 33 | } | 35 | } |
| 34 | shared_memory.header.entry_count = 16; | ||
| 35 | 36 | ||
| 36 | const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; | 37 | const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state; |
| 37 | shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; | 38 | next_state.sampling_number = last_entry.sampling_number + 1; |
| 38 | auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; | ||
| 39 | |||
| 40 | cur_entry.sampling_number = last_entry.sampling_number + 1; | ||
| 41 | cur_entry.sampling_number2 = cur_entry.sampling_number; | ||
| 42 | 39 | ||
| 43 | if (Settings::values.debug_pad_enabled) { | 40 | if (Settings::values.debug_pad_enabled) { |
| 44 | cur_entry.attribute.connected.Assign(1); | 41 | next_state.attribute.connected.Assign(1); |
| 45 | auto& pad = cur_entry.pad_state; | ||
| 46 | 42 | ||
| 47 | using namespace Settings::NativeButton; | 43 | const auto& button_state = controller->GetDebugPadButtons(); |
| 48 | pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); | 44 | const auto& stick_state = controller->GetSticks(); |
| 49 | pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 50 | pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 51 | pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 52 | pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 53 | pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 54 | pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 55 | pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 56 | pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 57 | pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 58 | pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 59 | pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 60 | pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 61 | pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 62 | 45 | ||
| 63 | const auto [stick_l_x_f, stick_l_y_f] = | 46 | next_state.pad_state = button_state; |
| 64 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | 47 | next_state.l_stick = stick_state.left; |
| 65 | const auto [stick_r_x_f, stick_r_y_f] = | 48 | next_state.r_stick = stick_state.right; |
| 66 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 67 | cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | ||
| 68 | cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | ||
| 69 | cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | ||
| 70 | cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | ||
| 71 | } | 49 | } |
| 72 | 50 | ||
| 73 | std::memcpy(data, &shared_memory, sizeof(SharedMemory)); | 51 | debug_pad_lifo.WriteNextEntry(next_state); |
| 52 | std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo)); | ||
| 74 | } | 53 | } |
| 75 | 54 | ||
| 76 | void Controller_DebugPad::OnLoadInputDevices() { | ||
| 77 | std::transform(Settings::values.debug_pad_buttons.begin(), | ||
| 78 | Settings::values.debug_pad_buttons.begin() + | ||
| 79 | Settings::NativeButton::NUM_BUTTONS_HID, | ||
| 80 | buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 81 | std::transform(Settings::values.debug_pad_analogs.begin(), | ||
| 82 | Settings::values.debug_pad_analogs.end(), analogs.begin(), | ||
| 83 | Input::CreateDevice<Input::AnalogDevice>); | ||
| 84 | } | ||
| 85 | } // namespace Service::HID | 55 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 1b1645184..afe374fc2 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h | |||
| @@ -8,15 +8,20 @@ | |||
| 8 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/settings.h" | ||
| 12 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 13 | #include "core/frontend/input.h" | ||
| 14 | #include "core/hle/service/hid/controllers/controller_base.h" | 12 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 13 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 14 | |||
| 15 | namespace Core::HID { | ||
| 16 | class EmulatedController; | ||
| 17 | struct DebugPadButton; | ||
| 18 | struct AnalogStickState; | ||
| 19 | } // namespace Core::HID | ||
| 15 | 20 | ||
| 16 | namespace Service::HID { | 21 | namespace Service::HID { |
| 17 | class Controller_DebugPad final : public ControllerBase { | 22 | class Controller_DebugPad final : public ControllerBase { |
| 18 | public: | 23 | public: |
| 19 | explicit Controller_DebugPad(Core::System& system_); | 24 | explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_); |
| 20 | ~Controller_DebugPad() override; | 25 | ~Controller_DebugPad() override; |
| 21 | 26 | ||
| 22 | // Called when the controller is initialized | 27 | // Called when the controller is initialized |
| @@ -28,66 +33,31 @@ public: | |||
| 28 | // When the controller is requesting an update for the shared memory | 33 | // When the controller is requesting an update for the shared memory |
| 29 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; | 34 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; |
| 30 | 35 | ||
| 31 | // Called when input devices should be loaded | ||
| 32 | void OnLoadInputDevices() override; | ||
| 33 | |||
| 34 | private: | 36 | private: |
| 35 | struct AnalogStick { | 37 | // This is nn::hid::DebugPadAttribute |
| 36 | s32_le x; | 38 | struct DebugPadAttribute { |
| 37 | s32_le y; | ||
| 38 | }; | ||
| 39 | static_assert(sizeof(AnalogStick) == 0x8); | ||
| 40 | |||
| 41 | struct PadState { | ||
| 42 | union { | ||
| 43 | u32_le raw{}; | ||
| 44 | BitField<0, 1, u32> a; | ||
| 45 | BitField<1, 1, u32> b; | ||
| 46 | BitField<2, 1, u32> x; | ||
| 47 | BitField<3, 1, u32> y; | ||
| 48 | BitField<4, 1, u32> l; | ||
| 49 | BitField<5, 1, u32> r; | ||
| 50 | BitField<6, 1, u32> zl; | ||
| 51 | BitField<7, 1, u32> zr; | ||
| 52 | BitField<8, 1, u32> plus; | ||
| 53 | BitField<9, 1, u32> minus; | ||
| 54 | BitField<10, 1, u32> d_left; | ||
| 55 | BitField<11, 1, u32> d_up; | ||
| 56 | BitField<12, 1, u32> d_right; | ||
| 57 | BitField<13, 1, u32> d_down; | ||
| 58 | }; | ||
| 59 | }; | ||
| 60 | static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size"); | ||
| 61 | |||
| 62 | struct Attributes { | ||
| 63 | union { | 39 | union { |
| 64 | u32_le raw{}; | 40 | u32 raw{}; |
| 65 | BitField<0, 1, u32> connected; | 41 | BitField<0, 1, u32> connected; |
| 66 | }; | 42 | }; |
| 67 | }; | 43 | }; |
| 68 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); | 44 | static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size"); |
| 69 | 45 | ||
| 70 | struct PadStates { | 46 | // This is nn::hid::DebugPadState |
| 71 | s64_le sampling_number; | 47 | struct DebugPadState { |
| 72 | s64_le sampling_number2; | 48 | s64 sampling_number; |
| 73 | Attributes attribute; | 49 | DebugPadAttribute attribute; |
| 74 | PadState pad_state; | 50 | Core::HID::DebugPadButton pad_state; |
| 75 | AnalogStick r_stick; | 51 | Core::HID::AnalogStickState r_stick; |
| 76 | AnalogStick l_stick; | 52 | Core::HID::AnalogStickState l_stick; |
| 77 | }; | 53 | }; |
| 78 | static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state"); | 54 | static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state"); |
| 79 | 55 | ||
| 80 | struct SharedMemory { | 56 | // This is nn::hid::detail::DebugPadLifo |
| 81 | CommonHeader header; | 57 | Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{}; |
| 82 | std::array<PadStates, 17> pad_states; | 58 | static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); |
| 83 | INSERT_PADDING_BYTES(0x138); | 59 | DebugPadState next_state{}; |
| 84 | }; | ||
| 85 | static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); | ||
| 86 | SharedMemory shared_memory{}; | ||
| 87 | 60 | ||
| 88 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | 61 | Core::HID::EmulatedController* controller; |
| 89 | buttons; | ||
| 90 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> | ||
| 91 | analogs; | ||
| 92 | }; | 62 | }; |
| 93 | } // namespace Service::HID | 63 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 764abb5b6..fe895c4f6 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/settings.h" | 7 | #include "common/settings.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/frontend/emu_window.h" | 9 | #include "core/frontend/emu_window.h" |
| 10 | #include "core/hid/hid_core.h" | ||
| 10 | #include "core/hle/service/hid/controllers/gesture.h" | 11 | #include "core/hle/service/hid/controllers/gesture.h" |
| 11 | 12 | ||
| 12 | namespace Service::HID { | 13 | namespace Service::HID { |
| @@ -23,16 +24,14 @@ constexpr f32 Square(s32 num) { | |||
| 23 | return static_cast<f32>(num * num); | 24 | return static_cast<f32>(num * num); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {} | 27 | Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { |
| 28 | console = hid_core.GetEmulatedConsole(); | ||
| 29 | } | ||
| 27 | Controller_Gesture::~Controller_Gesture() = default; | 30 | Controller_Gesture::~Controller_Gesture() = default; |
| 28 | 31 | ||
| 29 | void Controller_Gesture::OnInit() { | 32 | void Controller_Gesture::OnInit() { |
| 30 | for (std::size_t id = 0; id < MAX_FINGERS; ++id) { | 33 | gesture_lifo.buffer_count = 0; |
| 31 | mouse_finger_id[id] = MAX_POINTS; | 34 | gesture_lifo.buffer_tail = 0; |
| 32 | keyboard_finger_id[id] = MAX_POINTS; | ||
| 33 | udp_finger_id[id] = MAX_POINTS; | ||
| 34 | } | ||
| 35 | shared_memory.header.entry_count = 0; | ||
| 36 | force_update = true; | 35 | force_update = true; |
| 37 | } | 36 | } |
| 38 | 37 | ||
| @@ -40,50 +39,38 @@ void Controller_Gesture::OnRelease() {} | |||
| 40 | 39 | ||
| 41 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 40 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 42 | std::size_t size) { | 41 | std::size_t size) { |
| 43 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); | ||
| 44 | shared_memory.header.total_entry_count = 17; | ||
| 45 | |||
| 46 | if (!IsControllerActivated()) { | 42 | if (!IsControllerActivated()) { |
| 47 | shared_memory.header.entry_count = 0; | 43 | gesture_lifo.buffer_count = 0; |
| 48 | shared_memory.header.last_entry_index = 0; | 44 | gesture_lifo.buffer_tail = 0; |
| 45 | std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo)); | ||
| 49 | return; | 46 | return; |
| 50 | } | 47 | } |
| 51 | 48 | ||
| 52 | ReadTouchInput(); | 49 | ReadTouchInput(); |
| 53 | 50 | ||
| 54 | GestureProperties gesture = GetGestureProperties(); | 51 | GestureProperties gesture = GetGestureProperties(); |
| 55 | f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) / | 52 | f32 time_difference = |
| 56 | (1000 * 1000 * 1000); | 53 | static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000); |
| 57 | 54 | ||
| 58 | // Only update if necesary | 55 | // Only update if necesary |
| 59 | if (!ShouldUpdateGesture(gesture, time_difference)) { | 56 | if (!ShouldUpdateGesture(gesture, time_difference)) { |
| 60 | return; | 57 | return; |
| 61 | } | 58 | } |
| 62 | 59 | ||
| 63 | last_update_timestamp = shared_memory.header.timestamp; | 60 | last_update_timestamp = gesture_lifo.timestamp; |
| 64 | UpdateGestureSharedMemory(data, size, gesture, time_difference); | 61 | UpdateGestureSharedMemory(data, size, gesture, time_difference); |
| 65 | } | 62 | } |
| 66 | 63 | ||
| 67 | void Controller_Gesture::ReadTouchInput() { | 64 | void Controller_Gesture::ReadTouchInput() { |
| 68 | const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); | 65 | const auto touch_status = console->GetTouch(); |
| 69 | const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); | 66 | for (std::size_t id = 0; id < fingers.size(); ++id) { |
| 70 | for (std::size_t id = 0; id < mouse_status.size(); ++id) { | 67 | fingers[id] = touch_status[id]; |
| 71 | mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); | ||
| 72 | udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); | ||
| 73 | } | ||
| 74 | |||
| 75 | if (Settings::values.use_touch_from_button) { | ||
| 76 | const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus(); | ||
| 77 | for (std::size_t id = 0; id < mouse_status.size(); ++id) { | ||
| 78 | keyboard_finger_id[id] = | ||
| 79 | UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]); | ||
| 80 | } | ||
| 81 | } | 68 | } |
| 82 | } | 69 | } |
| 83 | 70 | ||
| 84 | bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, | 71 | bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, |
| 85 | f32 time_difference) { | 72 | f32 time_difference) { |
| 86 | const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; | 73 | const auto& last_entry = GetLastGestureEntry(); |
| 87 | if (force_update) { | 74 | if (force_update) { |
| 88 | force_update = false; | 75 | force_update = false; |
| 89 | return true; | 76 | return true; |
| @@ -97,7 +84,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, | |||
| 97 | } | 84 | } |
| 98 | 85 | ||
| 99 | // Update on press and hold event after 0.5 seconds | 86 | // Update on press and hold event after 0.5 seconds |
| 100 | if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 && | 87 | if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 && |
| 101 | time_difference > press_delay) { | 88 | time_difference > press_delay) { |
| 102 | return enable_press_and_tap; | 89 | return enable_press_and_tap; |
| 103 | } | 90 | } |
| @@ -108,27 +95,19 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, | |||
| 108 | void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, | 95 | void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, |
| 109 | GestureProperties& gesture, | 96 | GestureProperties& gesture, |
| 110 | f32 time_difference) { | 97 | f32 time_difference) { |
| 111 | TouchType type = TouchType::Idle; | 98 | GestureType type = GestureType::Idle; |
| 112 | Attribute attributes{}; | 99 | GestureAttribute attributes{}; |
| 113 | 100 | ||
| 114 | const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; | 101 | const auto& last_entry = gesture_lifo.ReadCurrentEntry().state; |
| 115 | shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; | ||
| 116 | auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; | ||
| 117 | 102 | ||
| 118 | if (shared_memory.header.entry_count < 16) { | 103 | // Reset next state to default |
| 119 | shared_memory.header.entry_count++; | 104 | next_state.sampling_number = last_entry.sampling_number + 1; |
| 120 | } | 105 | next_state.delta = {}; |
| 121 | 106 | next_state.vel_x = 0; | |
| 122 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 107 | next_state.vel_y = 0; |
| 123 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 108 | next_state.direction = GestureDirection::None; |
| 124 | 109 | next_state.rotation_angle = 0; | |
| 125 | // Reset values to default | 110 | next_state.scale = 0; |
| 126 | cur_entry.delta = {}; | ||
| 127 | cur_entry.vel_x = 0; | ||
| 128 | cur_entry.vel_y = 0; | ||
| 129 | cur_entry.direction = Direction::None; | ||
| 130 | cur_entry.rotation_angle = 0; | ||
| 131 | cur_entry.scale = 0; | ||
| 132 | 111 | ||
| 133 | if (gesture.active_points > 0) { | 112 | if (gesture.active_points > 0) { |
| 134 | if (last_gesture.active_points == 0) { | 113 | if (last_gesture.active_points == 0) { |
| @@ -141,46 +120,47 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, | |||
| 141 | } | 120 | } |
| 142 | 121 | ||
| 143 | // Apply attributes | 122 | // Apply attributes |
| 144 | cur_entry.detection_count = gesture.detection_count; | 123 | next_state.detection_count = gesture.detection_count; |
| 145 | cur_entry.type = type; | 124 | next_state.type = type; |
| 146 | cur_entry.attributes = attributes; | 125 | next_state.attributes = attributes; |
| 147 | cur_entry.pos = gesture.mid_point; | 126 | next_state.pos = gesture.mid_point; |
| 148 | cur_entry.point_count = static_cast<s32>(gesture.active_points); | 127 | next_state.point_count = static_cast<s32>(gesture.active_points); |
| 149 | cur_entry.points = gesture.points; | 128 | next_state.points = gesture.points; |
| 150 | last_gesture = gesture; | 129 | last_gesture = gesture; |
| 151 | 130 | ||
| 152 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); | 131 | gesture_lifo.WriteNextEntry(next_state); |
| 132 | std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo)); | ||
| 153 | } | 133 | } |
| 154 | 134 | ||
| 155 | void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type, | 135 | void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, |
| 156 | Attribute& attributes) { | 136 | GestureAttribute& attributes) { |
| 157 | const auto& last_entry = GetLastGestureEntry(); | 137 | const auto& last_entry = GetLastGestureEntry(); |
| 158 | 138 | ||
| 159 | gesture.detection_count++; | 139 | gesture.detection_count++; |
| 160 | type = TouchType::Touch; | 140 | type = GestureType::Touch; |
| 161 | 141 | ||
| 162 | // New touch after cancel is not considered new | 142 | // New touch after cancel is not considered new |
| 163 | if (last_entry.type != TouchType::Cancel) { | 143 | if (last_entry.type != GestureType::Cancel) { |
| 164 | attributes.is_new_touch.Assign(1); | 144 | attributes.is_new_touch.Assign(1); |
| 165 | enable_press_and_tap = true; | 145 | enable_press_and_tap = true; |
| 166 | } | 146 | } |
| 167 | } | 147 | } |
| 168 | 148 | ||
| 169 | void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type, | 149 | void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, |
| 170 | f32 time_difference) { | 150 | f32 time_difference) { |
| 171 | const auto& last_entry = GetLastGestureEntry(); | 151 | const auto& last_entry = GetLastGestureEntry(); |
| 172 | 152 | ||
| 173 | // Promote to pan type if touch moved | 153 | // Promote to pan type if touch moved |
| 174 | for (size_t id = 0; id < MAX_POINTS; id++) { | 154 | for (size_t id = 0; id < MAX_POINTS; id++) { |
| 175 | if (gesture.points[id] != last_gesture.points[id]) { | 155 | if (gesture.points[id] != last_gesture.points[id]) { |
| 176 | type = TouchType::Pan; | 156 | type = GestureType::Pan; |
| 177 | break; | 157 | break; |
| 178 | } | 158 | } |
| 179 | } | 159 | } |
| 180 | 160 | ||
| 181 | // Number of fingers changed cancel the last event and clear data | 161 | // Number of fingers changed cancel the last event and clear data |
| 182 | if (gesture.active_points != last_gesture.active_points) { | 162 | if (gesture.active_points != last_gesture.active_points) { |
| 183 | type = TouchType::Cancel; | 163 | type = GestureType::Cancel; |
| 184 | enable_press_and_tap = false; | 164 | enable_press_and_tap = false; |
| 185 | gesture.active_points = 0; | 165 | gesture.active_points = 0; |
| 186 | gesture.mid_point = {}; | 166 | gesture.mid_point = {}; |
| @@ -189,41 +169,41 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch | |||
| 189 | } | 169 | } |
| 190 | 170 | ||
| 191 | // Calculate extra parameters of panning | 171 | // Calculate extra parameters of panning |
| 192 | if (type == TouchType::Pan) { | 172 | if (type == GestureType::Pan) { |
| 193 | UpdatePanEvent(gesture, last_gesture, type, time_difference); | 173 | UpdatePanEvent(gesture, last_gesture, type, time_difference); |
| 194 | return; | 174 | return; |
| 195 | } | 175 | } |
| 196 | 176 | ||
| 197 | // Promote to press type | 177 | // Promote to press type |
| 198 | if (last_entry.type == TouchType::Touch) { | 178 | if (last_entry.type == GestureType::Touch) { |
| 199 | type = TouchType::Press; | 179 | type = GestureType::Press; |
| 200 | } | 180 | } |
| 201 | } | 181 | } |
| 202 | 182 | ||
| 203 | void Controller_Gesture::EndGesture(GestureProperties& gesture, | 183 | void Controller_Gesture::EndGesture(GestureProperties& gesture, |
| 204 | GestureProperties& last_gesture_props, TouchType& type, | 184 | GestureProperties& last_gesture_props, GestureType& type, |
| 205 | Attribute& attributes, f32 time_difference) { | 185 | GestureAttribute& attributes, f32 time_difference) { |
| 206 | const auto& last_entry = GetLastGestureEntry(); | 186 | const auto& last_entry = GetLastGestureEntry(); |
| 207 | 187 | ||
| 208 | if (last_gesture_props.active_points != 0) { | 188 | if (last_gesture_props.active_points != 0) { |
| 209 | switch (last_entry.type) { | 189 | switch (last_entry.type) { |
| 210 | case TouchType::Touch: | 190 | case GestureType::Touch: |
| 211 | if (enable_press_and_tap) { | 191 | if (enable_press_and_tap) { |
| 212 | SetTapEvent(gesture, last_gesture_props, type, attributes); | 192 | SetTapEvent(gesture, last_gesture_props, type, attributes); |
| 213 | return; | 193 | return; |
| 214 | } | 194 | } |
| 215 | type = TouchType::Cancel; | 195 | type = GestureType::Cancel; |
| 216 | force_update = true; | 196 | force_update = true; |
| 217 | break; | 197 | break; |
| 218 | case TouchType::Press: | 198 | case GestureType::Press: |
| 219 | case TouchType::Tap: | 199 | case GestureType::Tap: |
| 220 | case TouchType::Swipe: | 200 | case GestureType::Swipe: |
| 221 | case TouchType::Pinch: | 201 | case GestureType::Pinch: |
| 222 | case TouchType::Rotate: | 202 | case GestureType::Rotate: |
| 223 | type = TouchType::Complete; | 203 | type = GestureType::Complete; |
| 224 | force_update = true; | 204 | force_update = true; |
| 225 | break; | 205 | break; |
| 226 | case TouchType::Pan: | 206 | case GestureType::Pan: |
| 227 | EndPanEvent(gesture, last_gesture_props, type, time_difference); | 207 | EndPanEvent(gesture, last_gesture_props, type, time_difference); |
| 228 | break; | 208 | break; |
| 229 | default: | 209 | default: |
| @@ -231,15 +211,15 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture, | |||
| 231 | } | 211 | } |
| 232 | return; | 212 | return; |
| 233 | } | 213 | } |
| 234 | if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) { | 214 | if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) { |
| 235 | gesture.detection_count++; | 215 | gesture.detection_count++; |
| 236 | } | 216 | } |
| 237 | } | 217 | } |
| 238 | 218 | ||
| 239 | void Controller_Gesture::SetTapEvent(GestureProperties& gesture, | 219 | void Controller_Gesture::SetTapEvent(GestureProperties& gesture, |
| 240 | GestureProperties& last_gesture_props, TouchType& type, | 220 | GestureProperties& last_gesture_props, GestureType& type, |
| 241 | Attribute& attributes) { | 221 | GestureAttribute& attributes) { |
| 242 | type = TouchType::Tap; | 222 | type = GestureType::Tap; |
| 243 | gesture = last_gesture_props; | 223 | gesture = last_gesture_props; |
| 244 | force_update = true; | 224 | force_update = true; |
| 245 | f32 tap_time_difference = | 225 | f32 tap_time_difference = |
| @@ -251,44 +231,42 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture, | |||
| 251 | } | 231 | } |
| 252 | 232 | ||
| 253 | void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, | 233 | void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, |
| 254 | GestureProperties& last_gesture_props, TouchType& type, | 234 | GestureProperties& last_gesture_props, GestureType& type, |
| 255 | f32 time_difference) { | 235 | f32 time_difference) { |
| 256 | auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; | ||
| 257 | const auto& last_entry = GetLastGestureEntry(); | 236 | const auto& last_entry = GetLastGestureEntry(); |
| 258 | 237 | ||
| 259 | cur_entry.delta = gesture.mid_point - last_entry.pos; | 238 | next_state.delta = gesture.mid_point - last_entry.pos; |
| 260 | cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference; | 239 | next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; |
| 261 | cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference; | 240 | next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; |
| 262 | last_pan_time_difference = time_difference; | 241 | last_pan_time_difference = time_difference; |
| 263 | 242 | ||
| 264 | // Promote to pinch type | 243 | // Promote to pinch type |
| 265 | if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > | 244 | if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > |
| 266 | pinch_threshold) { | 245 | pinch_threshold) { |
| 267 | type = TouchType::Pinch; | 246 | type = GestureType::Pinch; |
| 268 | cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance; | 247 | next_state.scale = gesture.average_distance / last_gesture_props.average_distance; |
| 269 | } | 248 | } |
| 270 | 249 | ||
| 271 | const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / | 250 | const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / |
| 272 | (1 + (gesture.angle * last_gesture_props.angle))); | 251 | (1 + (gesture.angle * last_gesture_props.angle))); |
| 273 | // Promote to rotate type | 252 | // Promote to rotate type |
| 274 | if (std::abs(angle_between_two_lines) > angle_threshold) { | 253 | if (std::abs(angle_between_two_lines) > angle_threshold) { |
| 275 | type = TouchType::Rotate; | 254 | type = GestureType::Rotate; |
| 276 | cur_entry.scale = 0; | 255 | next_state.scale = 0; |
| 277 | cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; | 256 | next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; |
| 278 | } | 257 | } |
| 279 | } | 258 | } |
| 280 | 259 | ||
| 281 | void Controller_Gesture::EndPanEvent(GestureProperties& gesture, | 260 | void Controller_Gesture::EndPanEvent(GestureProperties& gesture, |
| 282 | GestureProperties& last_gesture_props, TouchType& type, | 261 | GestureProperties& last_gesture_props, GestureType& type, |
| 283 | f32 time_difference) { | 262 | f32 time_difference) { |
| 284 | auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; | ||
| 285 | const auto& last_entry = GetLastGestureEntry(); | 263 | const auto& last_entry = GetLastGestureEntry(); |
| 286 | cur_entry.vel_x = | 264 | next_state.vel_x = |
| 287 | static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); | 265 | static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); |
| 288 | cur_entry.vel_y = | 266 | next_state.vel_y = |
| 289 | static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); | 267 | static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); |
| 290 | const f32 curr_vel = | 268 | const f32 curr_vel = |
| 291 | std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y)); | 269 | std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); |
| 292 | 270 | ||
| 293 | // Set swipe event with parameters | 271 | // Set swipe event with parameters |
| 294 | if (curr_vel > swipe_threshold) { | 272 | if (curr_vel > swipe_threshold) { |
| @@ -297,105 +275,50 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture, | |||
| 297 | } | 275 | } |
| 298 | 276 | ||
| 299 | // End panning without swipe | 277 | // End panning without swipe |
| 300 | type = TouchType::Complete; | 278 | type = GestureType::Complete; |
| 301 | cur_entry.vel_x = 0; | 279 | next_state.vel_x = 0; |
| 302 | cur_entry.vel_y = 0; | 280 | next_state.vel_y = 0; |
| 303 | force_update = true; | 281 | force_update = true; |
| 304 | } | 282 | } |
| 305 | 283 | ||
| 306 | void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, | 284 | void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, |
| 307 | GestureProperties& last_gesture_props, TouchType& type) { | 285 | GestureProperties& last_gesture_props, GestureType& type) { |
| 308 | auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; | ||
| 309 | const auto& last_entry = GetLastGestureEntry(); | 286 | const auto& last_entry = GetLastGestureEntry(); |
| 310 | 287 | ||
| 311 | type = TouchType::Swipe; | 288 | type = GestureType::Swipe; |
| 312 | gesture = last_gesture_props; | 289 | gesture = last_gesture_props; |
| 313 | force_update = true; | 290 | force_update = true; |
| 314 | cur_entry.delta = last_entry.delta; | 291 | next_state.delta = last_entry.delta; |
| 315 | 292 | ||
| 316 | if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) { | 293 | if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { |
| 317 | if (cur_entry.delta.x > 0) { | 294 | if (next_state.delta.x > 0) { |
| 318 | cur_entry.direction = Direction::Right; | 295 | next_state.direction = GestureDirection::Right; |
| 319 | return; | 296 | return; |
| 320 | } | 297 | } |
| 321 | cur_entry.direction = Direction::Left; | 298 | next_state.direction = GestureDirection::Left; |
| 322 | return; | 299 | return; |
| 323 | } | 300 | } |
| 324 | if (cur_entry.delta.y > 0) { | 301 | if (next_state.delta.y > 0) { |
| 325 | cur_entry.direction = Direction::Down; | 302 | next_state.direction = GestureDirection::Down; |
| 326 | return; | 303 | return; |
| 327 | } | 304 | } |
| 328 | cur_entry.direction = Direction::Up; | 305 | next_state.direction = GestureDirection::Up; |
| 329 | } | ||
| 330 | |||
| 331 | void Controller_Gesture::OnLoadInputDevices() { | ||
| 332 | touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window"); | ||
| 333 | touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp"); | ||
| 334 | touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); | ||
| 335 | } | ||
| 336 | |||
| 337 | std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const { | ||
| 338 | // Dont assign any touch input to a point if disabled | ||
| 339 | if (!Settings::values.touchscreen.enabled) { | ||
| 340 | return std::nullopt; | ||
| 341 | } | ||
| 342 | std::size_t first_free_id = 0; | ||
| 343 | while (first_free_id < MAX_POINTS) { | ||
| 344 | if (!fingers[first_free_id].pressed) { | ||
| 345 | return first_free_id; | ||
| 346 | } else { | ||
| 347 | first_free_id++; | ||
| 348 | } | ||
| 349 | } | ||
| 350 | return std::nullopt; | ||
| 351 | } | ||
| 352 | |||
| 353 | Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() { | ||
| 354 | return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; | ||
| 355 | } | 306 | } |
| 356 | 307 | ||
| 357 | const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { | 308 | const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { |
| 358 | return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; | 309 | return gesture_lifo.ReadCurrentEntry().state; |
| 359 | } | ||
| 360 | |||
| 361 | std::size_t Controller_Gesture::UpdateTouchInputEvent( | ||
| 362 | const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) { | ||
| 363 | const auto& [x, y, pressed] = touch_input; | ||
| 364 | if (finger_id > MAX_POINTS) { | ||
| 365 | LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id); | ||
| 366 | return MAX_POINTS; | ||
| 367 | } | ||
| 368 | if (pressed) { | ||
| 369 | if (finger_id == MAX_POINTS) { | ||
| 370 | const auto first_free_id = GetUnusedFingerID(); | ||
| 371 | if (!first_free_id) { | ||
| 372 | // Invalid finger id do nothing | ||
| 373 | return MAX_POINTS; | ||
| 374 | } | ||
| 375 | finger_id = first_free_id.value(); | ||
| 376 | fingers[finger_id].pressed = true; | ||
| 377 | } | ||
| 378 | fingers[finger_id].pos = {x, y}; | ||
| 379 | return finger_id; | ||
| 380 | } | ||
| 381 | |||
| 382 | if (finger_id != MAX_POINTS) { | ||
| 383 | fingers[finger_id].pressed = false; | ||
| 384 | } | ||
| 385 | |||
| 386 | return MAX_POINTS; | ||
| 387 | } | 310 | } |
| 388 | 311 | ||
| 389 | Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { | 312 | Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { |
| 390 | GestureProperties gesture; | 313 | GestureProperties gesture; |
| 391 | std::array<Finger, MAX_POINTS> active_fingers; | 314 | std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; |
| 392 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | 315 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), |
| 393 | [](const auto& finger) { return finger.pressed; }); | 316 | [](const auto& finger) { return finger.pressed; }); |
| 394 | gesture.active_points = | 317 | gesture.active_points = |
| 395 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | 318 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); |
| 396 | 319 | ||
| 397 | for (size_t id = 0; id < gesture.active_points; ++id) { | 320 | for (size_t id = 0; id < gesture.active_points; ++id) { |
| 398 | const auto& [active_x, active_y] = active_fingers[id].pos; | 321 | const auto& [active_x, active_y] = active_fingers[id].position; |
| 399 | gesture.points[id] = { | 322 | gesture.points[id] = { |
| 400 | .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), | 323 | .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), |
| 401 | .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), | 324 | .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), |
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 7e7ae6625..0936a3fa3 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/point.h" | 10 | #include "common/point.h" |
| 11 | #include "core/frontend/input.h" | 11 | #include "core/hid/emulated_console.h" |
| 12 | #include "core/hle/service/hid/controllers/controller_base.h" | 12 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 13 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 13 | 14 | ||
| 14 | namespace Service::HID { | 15 | namespace Service::HID { |
| 15 | class Controller_Gesture final : public ControllerBase { | 16 | class Controller_Gesture final : public ControllerBase { |
| 16 | public: | 17 | public: |
| 17 | explicit Controller_Gesture(Core::System& system_); | 18 | explicit Controller_Gesture(Core::HID::HIDCore& hid_core_); |
| 18 | ~Controller_Gesture() override; | 19 | ~Controller_Gesture() override; |
| 19 | 20 | ||
| 20 | // Called when the controller is initialized | 21 | // Called when the controller is initialized |
| @@ -26,14 +27,12 @@ public: | |||
| 26 | // When the controller is requesting an update for the shared memory | 27 | // When the controller is requesting an update for the shared memory |
| 27 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; | 28 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; |
| 28 | 29 | ||
| 29 | // Called when input devices should be loaded | ||
| 30 | void OnLoadInputDevices() override; | ||
| 31 | |||
| 32 | private: | 30 | private: |
| 33 | static constexpr size_t MAX_FINGERS = 16; | 31 | static constexpr size_t MAX_FINGERS = 16; |
| 34 | static constexpr size_t MAX_POINTS = 4; | 32 | static constexpr size_t MAX_POINTS = 4; |
| 35 | 33 | ||
| 36 | enum class TouchType : u32 { | 34 | // This is nn::hid::GestureType |
| 35 | enum class GestureType : u32 { | ||
| 37 | Idle, // Nothing touching the screen | 36 | Idle, // Nothing touching the screen |
| 38 | Complete, // Set at the end of a touch event | 37 | Complete, // Set at the end of a touch event |
| 39 | Cancel, // Set when the number of fingers change | 38 | Cancel, // Set when the number of fingers change |
| @@ -46,7 +45,8 @@ private: | |||
| 46 | Rotate, // All points rotating from the midpoint | 45 | Rotate, // All points rotating from the midpoint |
| 47 | }; | 46 | }; |
| 48 | 47 | ||
| 49 | enum class Direction : u32 { | 48 | // This is nn::hid::GestureDirection |
| 49 | enum class GestureDirection : u32 { | ||
| 50 | None, | 50 | None, |
| 51 | Left, | 51 | Left, |
| 52 | Up, | 52 | Up, |
| @@ -54,51 +54,41 @@ private: | |||
| 54 | Down, | 54 | Down, |
| 55 | }; | 55 | }; |
| 56 | 56 | ||
| 57 | struct Attribute { | 57 | // This is nn::hid::GestureAttribute |
| 58 | struct GestureAttribute { | ||
| 58 | union { | 59 | union { |
| 59 | u32_le raw{}; | 60 | u32 raw{}; |
| 60 | 61 | ||
| 61 | BitField<4, 1, u32> is_new_touch; | 62 | BitField<4, 1, u32> is_new_touch; |
| 62 | BitField<8, 1, u32> is_double_tap; | 63 | BitField<8, 1, u32> is_double_tap; |
| 63 | }; | 64 | }; |
| 64 | }; | 65 | }; |
| 65 | static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size"); | 66 | static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); |
| 66 | 67 | ||
| 68 | // This is nn::hid::GestureState | ||
| 67 | struct GestureState { | 69 | struct GestureState { |
| 68 | s64_le sampling_number; | 70 | s64 sampling_number; |
| 69 | s64_le sampling_number2; | 71 | s64 detection_count; |
| 70 | s64_le detection_count; | 72 | GestureType type; |
| 71 | TouchType type; | 73 | GestureDirection direction; |
| 72 | Direction direction; | 74 | Common::Point<s32> pos; |
| 73 | Common::Point<s32_le> pos; | 75 | Common::Point<s32> delta; |
| 74 | Common::Point<s32_le> delta; | ||
| 75 | f32 vel_x; | 76 | f32 vel_x; |
| 76 | f32 vel_y; | 77 | f32 vel_y; |
| 77 | Attribute attributes; | 78 | GestureAttribute attributes; |
| 78 | f32 scale; | 79 | f32 scale; |
| 79 | f32 rotation_angle; | 80 | f32 rotation_angle; |
| 80 | s32_le point_count; | 81 | s32 point_count; |
| 81 | std::array<Common::Point<s32_le>, 4> points; | 82 | std::array<Common::Point<s32>, 4> points; |
| 82 | }; | ||
| 83 | static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size"); | ||
| 84 | |||
| 85 | struct SharedMemory { | ||
| 86 | CommonHeader header; | ||
| 87 | std::array<GestureState, 17> gesture_states; | ||
| 88 | }; | ||
| 89 | static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size"); | ||
| 90 | |||
| 91 | struct Finger { | ||
| 92 | Common::Point<f32> pos{}; | ||
| 93 | bool pressed{}; | ||
| 94 | }; | 83 | }; |
| 84 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); | ||
| 95 | 85 | ||
| 96 | struct GestureProperties { | 86 | struct GestureProperties { |
| 97 | std::array<Common::Point<s32_le>, MAX_POINTS> points{}; | 87 | std::array<Common::Point<s32>, MAX_POINTS> points{}; |
| 98 | std::size_t active_points{}; | 88 | std::size_t active_points{}; |
| 99 | Common::Point<s32_le> mid_point{}; | 89 | Common::Point<s32> mid_point{}; |
| 100 | s64_le detection_count{}; | 90 | s64 detection_count{}; |
| 101 | u64_le delta_time{}; | 91 | u64 delta_time{}; |
| 102 | f32 average_distance{}; | 92 | f32 average_distance{}; |
| 103 | f32 angle{}; | 93 | f32 angle{}; |
| 104 | }; | 94 | }; |
| @@ -114,61 +104,48 @@ private: | |||
| 114 | f32 time_difference); | 104 | f32 time_difference); |
| 115 | 105 | ||
| 116 | // Initializes new gesture | 106 | // Initializes new gesture |
| 117 | void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes); | 107 | void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); |
| 118 | 108 | ||
| 119 | // Updates existing gesture state | 109 | // Updates existing gesture state |
| 120 | void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference); | 110 | void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference); |
| 121 | 111 | ||
| 122 | // Terminates exiting gesture | 112 | // Terminates exiting gesture |
| 123 | void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, | 113 | void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 124 | TouchType& type, Attribute& attributes, f32 time_difference); | 114 | GestureType& type, GestureAttribute& attributes, f32 time_difference); |
| 125 | 115 | ||
| 126 | // Set current event to a tap event | 116 | // Set current event to a tap event |
| 127 | void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 117 | void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 128 | TouchType& type, Attribute& attributes); | 118 | GestureType& type, GestureAttribute& attributes); |
| 129 | 119 | ||
| 130 | // Calculates and set the extra parameters related to a pan event | 120 | // Calculates and set the extra parameters related to a pan event |
| 131 | void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 121 | void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 132 | TouchType& type, f32 time_difference); | 122 | GestureType& type, f32 time_difference); |
| 133 | 123 | ||
| 134 | // Terminates the pan event | 124 | // Terminates the pan event |
| 135 | void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 125 | void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 136 | TouchType& type, f32 time_difference); | 126 | GestureType& type, f32 time_difference); |
| 137 | 127 | ||
| 138 | // Set current event to a swipe event | 128 | // Set current event to a swipe event |
| 139 | void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 129 | void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 140 | TouchType& type); | 130 | GestureType& type); |
| 141 | |||
| 142 | // Returns an unused finger id, if there is no fingers available std::nullopt is returned. | ||
| 143 | [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const; | ||
| 144 | 131 | ||
| 145 | // Retrieves the last gesture entry, as indicated by shared memory indices. | 132 | // Retrieves the last gesture entry, as indicated by shared memory indices. |
| 146 | [[nodiscard]] GestureState& GetLastGestureEntry(); | ||
| 147 | [[nodiscard]] const GestureState& GetLastGestureEntry() const; | 133 | [[nodiscard]] const GestureState& GetLastGestureEntry() const; |
| 148 | 134 | ||
| 149 | /** | ||
| 150 | * If the touch is new it tries to assign a new finger id, if there is no fingers available no | ||
| 151 | * changes will be made. Updates the coordinates if the finger id it's already set. If the touch | ||
| 152 | * ends delays the output by one frame to set the end_touch flag before finally freeing the | ||
| 153 | * finger id | ||
| 154 | */ | ||
| 155 | size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input, | ||
| 156 | size_t finger_id); | ||
| 157 | |||
| 158 | // Returns the average distance, angle and middle point of the active fingers | 135 | // Returns the average distance, angle and middle point of the active fingers |
| 159 | GestureProperties GetGestureProperties(); | 136 | GestureProperties GetGestureProperties(); |
| 160 | 137 | ||
| 161 | SharedMemory shared_memory{}; | 138 | // This is nn::hid::detail::GestureLifo |
| 162 | std::unique_ptr<Input::TouchDevice> touch_mouse_device; | 139 | Lifo<GestureState, hid_entry_count> gesture_lifo{}; |
| 163 | std::unique_ptr<Input::TouchDevice> touch_udp_device; | 140 | static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size"); |
| 164 | std::unique_ptr<Input::TouchDevice> touch_btn_device; | 141 | GestureState next_state{}; |
| 165 | std::array<size_t, MAX_FINGERS> mouse_finger_id{}; | 142 | |
| 166 | std::array<size_t, MAX_FINGERS> keyboard_finger_id{}; | 143 | Core::HID::EmulatedConsole* console; |
| 167 | std::array<size_t, MAX_FINGERS> udp_finger_id{}; | 144 | |
| 168 | std::array<Finger, MAX_POINTS> fingers{}; | 145 | std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; |
| 169 | GestureProperties last_gesture{}; | 146 | GestureProperties last_gesture{}; |
| 170 | s64_le last_update_timestamp{}; | 147 | s64 last_update_timestamp{}; |
| 171 | s64_le last_tap_timestamp{}; | 148 | s64 last_tap_timestamp{}; |
| 172 | f32 last_pan_time_difference{}; | 149 | f32 last_pan_time_difference{}; |
| 173 | bool force_update{false}; | 150 | bool force_update{false}; |
| 174 | bool enable_press_and_tap{false}; | 151 | bool enable_press_and_tap{false}; |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index c6c620008..9588a6910 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -6,13 +6,18 @@ | |||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/settings.h" | 7 | #include "common/settings.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/hid/emulated_devices.h" | ||
| 10 | #include "core/hid/hid_core.h" | ||
| 9 | #include "core/hle/service/hid/controllers/keyboard.h" | 11 | #include "core/hle/service/hid/controllers/keyboard.h" |
| 10 | 12 | ||
| 11 | namespace Service::HID { | 13 | namespace Service::HID { |
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; | 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; |
| 13 | constexpr u8 KEYS_PER_BYTE = 8; | ||
| 14 | 15 | ||
| 15 | Controller_Keyboard::Controller_Keyboard(Core::System& system_) : ControllerBase{system_} {} | 16 | Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_) |
| 17 | : ControllerBase{hid_core_} { | ||
| 18 | emulated_devices = hid_core.GetEmulatedDevices(); | ||
| 19 | } | ||
| 20 | |||
| 16 | Controller_Keyboard::~Controller_Keyboard() = default; | 21 | Controller_Keyboard::~Controller_Keyboard() = default; |
| 17 | 22 | ||
| 18 | void Controller_Keyboard::OnInit() {} | 23 | void Controller_Keyboard::OnInit() {} |
| @@ -21,51 +26,27 @@ void Controller_Keyboard::OnRelease() {} | |||
| 21 | 26 | ||
| 22 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 27 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 23 | std::size_t size) { | 28 | std::size_t size) { |
| 24 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); | ||
| 25 | shared_memory.header.total_entry_count = 17; | ||
| 26 | |||
| 27 | if (!IsControllerActivated()) { | 29 | if (!IsControllerActivated()) { |
| 28 | shared_memory.header.entry_count = 0; | 30 | keyboard_lifo.buffer_count = 0; |
| 29 | shared_memory.header.last_entry_index = 0; | 31 | keyboard_lifo.buffer_tail = 0; |
| 32 | std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo)); | ||
| 30 | return; | 33 | return; |
| 31 | } | 34 | } |
| 32 | shared_memory.header.entry_count = 16; | ||
| 33 | |||
| 34 | const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; | ||
| 35 | shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; | ||
| 36 | auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; | ||
| 37 | 35 | ||
| 38 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 36 | const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state; |
| 39 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 37 | next_state.sampling_number = last_entry.sampling_number + 1; |
| 40 | 38 | ||
| 41 | cur_entry.key.fill(0); | ||
| 42 | if (Settings::values.keyboard_enabled) { | 39 | if (Settings::values.keyboard_enabled) { |
| 43 | for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { | 40 | const auto& keyboard_state = emulated_devices->GetKeyboard(); |
| 44 | auto& entry = cur_entry.key[i / KEYS_PER_BYTE]; | 41 | const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier(); |
| 45 | entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE))); | ||
| 46 | } | ||
| 47 | 42 | ||
| 48 | using namespace Settings::NativeKeyboard; | 43 | next_state.key = keyboard_state; |
| 49 | 44 | next_state.modifier = keyboard_modifier_state; | |
| 50 | // TODO: Assign the correct key to all modifiers | 45 | next_state.attribute.is_connected.Assign(1); |
| 51 | cur_entry.modifier.control.Assign(keyboard_mods[LeftControl]->GetStatus()); | ||
| 52 | cur_entry.modifier.shift.Assign(keyboard_mods[LeftShift]->GetStatus()); | ||
| 53 | cur_entry.modifier.left_alt.Assign(keyboard_mods[LeftAlt]->GetStatus()); | ||
| 54 | cur_entry.modifier.right_alt.Assign(keyboard_mods[RightAlt]->GetStatus()); | ||
| 55 | cur_entry.modifier.gui.Assign(0); | ||
| 56 | cur_entry.modifier.caps_lock.Assign(keyboard_mods[CapsLock]->GetStatus()); | ||
| 57 | cur_entry.modifier.scroll_lock.Assign(keyboard_mods[ScrollLock]->GetStatus()); | ||
| 58 | cur_entry.modifier.num_lock.Assign(keyboard_mods[NumLock]->GetStatus()); | ||
| 59 | cur_entry.modifier.katakana.Assign(0); | ||
| 60 | cur_entry.modifier.hiragana.Assign(0); | ||
| 61 | } | 46 | } |
| 62 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); | ||
| 63 | } | ||
| 64 | 47 | ||
| 65 | void Controller_Keyboard::OnLoadInputDevices() { | 48 | keyboard_lifo.WriteNextEntry(next_state); |
| 66 | std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(), | 49 | std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo)); |
| 67 | keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 68 | std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(), | ||
| 69 | keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 70 | } | 50 | } |
| 51 | |||
| 71 | } // namespace Service::HID | 52 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 172a80e9c..cf62d3896 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h | |||
| @@ -8,15 +8,20 @@ | |||
| 8 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/settings.h" | ||
| 12 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 13 | #include "core/frontend/input.h" | ||
| 14 | #include "core/hle/service/hid/controllers/controller_base.h" | 12 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 13 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 14 | |||
| 15 | namespace Core::HID { | ||
| 16 | class EmulatedDevices; | ||
| 17 | struct KeyboardModifier; | ||
| 18 | struct KeyboardKey; | ||
| 19 | } // namespace Core::HID | ||
| 15 | 20 | ||
| 16 | namespace Service::HID { | 21 | namespace Service::HID { |
| 17 | class Controller_Keyboard final : public ControllerBase { | 22 | class Controller_Keyboard final : public ControllerBase { |
| 18 | public: | 23 | public: |
| 19 | explicit Controller_Keyboard(Core::System& system_); | 24 | explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_); |
| 20 | ~Controller_Keyboard() override; | 25 | ~Controller_Keyboard() override; |
| 21 | 26 | ||
| 22 | // Called when the controller is initialized | 27 | // Called when the controller is initialized |
| @@ -28,47 +33,21 @@ public: | |||
| 28 | // When the controller is requesting an update for the shared memory | 33 | // When the controller is requesting an update for the shared memory |
| 29 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; | 34 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; |
| 30 | 35 | ||
| 31 | // Called when input devices should be loaded | ||
| 32 | void OnLoadInputDevices() override; | ||
| 33 | |||
| 34 | private: | 36 | private: |
| 35 | struct Modifiers { | 37 | // This is nn::hid::detail::KeyboardState |
| 36 | union { | ||
| 37 | u32_le raw{}; | ||
| 38 | BitField<0, 1, u32> control; | ||
| 39 | BitField<1, 1, u32> shift; | ||
| 40 | BitField<2, 1, u32> left_alt; | ||
| 41 | BitField<3, 1, u32> right_alt; | ||
| 42 | BitField<4, 1, u32> gui; | ||
| 43 | BitField<8, 1, u32> caps_lock; | ||
| 44 | BitField<9, 1, u32> scroll_lock; | ||
| 45 | BitField<10, 1, u32> num_lock; | ||
| 46 | BitField<11, 1, u32> katakana; | ||
| 47 | BitField<12, 1, u32> hiragana; | ||
| 48 | }; | ||
| 49 | }; | ||
| 50 | static_assert(sizeof(Modifiers) == 0x4, "Modifiers is an invalid size"); | ||
| 51 | |||
| 52 | struct KeyboardState { | 38 | struct KeyboardState { |
| 53 | s64_le sampling_number; | 39 | s64 sampling_number; |
| 54 | s64_le sampling_number2; | 40 | Core::HID::KeyboardModifier modifier; |
| 55 | 41 | Core::HID::KeyboardAttribute attribute; | |
| 56 | Modifiers modifier; | 42 | Core::HID::KeyboardKey key; |
| 57 | std::array<u8, 32> key; | ||
| 58 | }; | 43 | }; |
| 59 | static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size"); | 44 | static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size"); |
| 60 | 45 | ||
| 61 | struct SharedMemory { | 46 | // This is nn::hid::detail::KeyboardLifo |
| 62 | CommonHeader header; | 47 | Lifo<KeyboardState, hid_entry_count> keyboard_lifo{}; |
| 63 | std::array<KeyboardState, 17> pad_states; | 48 | static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); |
| 64 | INSERT_PADDING_BYTES(0x28); | 49 | KeyboardState next_state{}; |
| 65 | }; | ||
| 66 | static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); | ||
| 67 | SharedMemory shared_memory{}; | ||
| 68 | 50 | ||
| 69 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys> | 51 | Core::HID::EmulatedDevices* emulated_devices; |
| 70 | keyboard_keys; | ||
| 71 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> | ||
| 72 | keyboard_mods; | ||
| 73 | }; | 52 | }; |
| 74 | } // namespace Service::HID | 53 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 544a71948..ba79888ae 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -6,12 +6,17 @@ | |||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/frontend/emu_window.h" | 8 | #include "core/frontend/emu_window.h" |
| 9 | #include "core/hid/emulated_devices.h" | ||
| 10 | #include "core/hid/hid_core.h" | ||
| 9 | #include "core/hle/service/hid/controllers/mouse.h" | 11 | #include "core/hle/service/hid/controllers/mouse.h" |
| 10 | 12 | ||
| 11 | namespace Service::HID { | 13 | namespace Service::HID { |
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; | 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; |
| 13 | 15 | ||
| 14 | Controller_Mouse::Controller_Mouse(Core::System& system_) : ControllerBase{system_} {} | 16 | Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { |
| 17 | emulated_devices = hid_core.GetEmulatedDevices(); | ||
| 18 | } | ||
| 19 | |||
| 15 | Controller_Mouse::~Controller_Mouse() = default; | 20 | Controller_Mouse::~Controller_Mouse() = default; |
| 16 | 21 | ||
| 17 | void Controller_Mouse::OnInit() {} | 22 | void Controller_Mouse::OnInit() {} |
| @@ -19,50 +24,35 @@ void Controller_Mouse::OnRelease() {} | |||
| 19 | 24 | ||
| 20 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 25 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 26 | std::size_t size) { |
| 22 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); | ||
| 23 | shared_memory.header.total_entry_count = 17; | ||
| 24 | |||
| 25 | if (!IsControllerActivated()) { | 27 | if (!IsControllerActivated()) { |
| 26 | shared_memory.header.entry_count = 0; | 28 | mouse_lifo.buffer_count = 0; |
| 27 | shared_memory.header.last_entry_index = 0; | 29 | mouse_lifo.buffer_tail = 0; |
| 30 | std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo)); | ||
| 28 | return; | 31 | return; |
| 29 | } | 32 | } |
| 30 | shared_memory.header.entry_count = 16; | ||
| 31 | 33 | ||
| 32 | auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index]; | 34 | const auto& last_entry = mouse_lifo.ReadCurrentEntry().state; |
| 33 | shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; | 35 | next_state.sampling_number = last_entry.sampling_number + 1; |
| 34 | auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index]; | ||
| 35 | 36 | ||
| 36 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 37 | next_state.attribute.raw = 0; |
| 37 | cur_entry.sampling_number2 = cur_entry.sampling_number; | ||
| 38 | |||
| 39 | cur_entry.attribute.raw = 0; | ||
| 40 | if (Settings::values.mouse_enabled) { | 38 | if (Settings::values.mouse_enabled) { |
| 41 | const auto [px, py, sx, sy] = mouse_device->GetStatus(); | 39 | const auto& mouse_button_state = emulated_devices->GetMouseButtons(); |
| 42 | const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width); | 40 | const auto& mouse_position_state = emulated_devices->GetMousePosition(); |
| 43 | const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height); | 41 | const auto& mouse_wheel_state = emulated_devices->GetMouseWheel(); |
| 44 | cur_entry.x = x; | 42 | next_state.attribute.is_connected.Assign(1); |
| 45 | cur_entry.y = y; | 43 | next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width); |
| 46 | cur_entry.delta_x = x - last_entry.x; | 44 | next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height); |
| 47 | cur_entry.delta_y = y - last_entry.y; | 45 | next_state.delta_x = next_state.x - last_entry.x; |
| 48 | cur_entry.mouse_wheel_x = sx; | 46 | next_state.delta_y = next_state.y - last_entry.y; |
| 49 | cur_entry.mouse_wheel_y = sy; | 47 | next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x; |
| 50 | cur_entry.attribute.is_connected.Assign(1); | 48 | next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y; |
| 51 | 49 | ||
| 52 | using namespace Settings::NativeMouseButton; | 50 | last_mouse_wheel_state = mouse_wheel_state; |
| 53 | cur_entry.button.left.Assign(mouse_button_devices[Left]->GetStatus()); | 51 | next_state.button = mouse_button_state; |
| 54 | cur_entry.button.right.Assign(mouse_button_devices[Right]->GetStatus()); | ||
| 55 | cur_entry.button.middle.Assign(mouse_button_devices[Middle]->GetStatus()); | ||
| 56 | cur_entry.button.forward.Assign(mouse_button_devices[Forward]->GetStatus()); | ||
| 57 | cur_entry.button.back.Assign(mouse_button_devices[Back]->GetStatus()); | ||
| 58 | } | 52 | } |
| 59 | 53 | ||
| 60 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); | 54 | mouse_lifo.WriteNextEntry(next_state); |
| 55 | std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo)); | ||
| 61 | } | 56 | } |
| 62 | 57 | ||
| 63 | void Controller_Mouse::OnLoadInputDevices() { | ||
| 64 | mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device); | ||
| 65 | std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(), | ||
| 66 | mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 67 | } | ||
| 68 | } // namespace Service::HID | 58 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 3d391a798..7559fc78d 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h | |||
| @@ -7,15 +7,20 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/settings.h" | ||
| 11 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 12 | #include "core/frontend/input.h" | ||
| 13 | #include "core/hle/service/hid/controllers/controller_base.h" | 11 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 12 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 13 | |||
| 14 | namespace Core::HID { | ||
| 15 | class EmulatedDevices; | ||
| 16 | struct MouseState; | ||
| 17 | struct AnalogStickState; | ||
| 18 | } // namespace Core::HID | ||
| 14 | 19 | ||
| 15 | namespace Service::HID { | 20 | namespace Service::HID { |
| 16 | class Controller_Mouse final : public ControllerBase { | 21 | class Controller_Mouse final : public ControllerBase { |
| 17 | public: | 22 | public: |
| 18 | explicit Controller_Mouse(Core::System& system_); | 23 | explicit Controller_Mouse(Core::HID::HIDCore& hid_core_); |
| 19 | ~Controller_Mouse() override; | 24 | ~Controller_Mouse() override; |
| 20 | 25 | ||
| 21 | // Called when the controller is initialized | 26 | // Called when the controller is initialized |
| @@ -27,53 +32,13 @@ public: | |||
| 27 | // When the controller is requesting an update for the shared memory | 32 | // When the controller is requesting an update for the shared memory |
| 28 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; | 33 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; |
| 29 | 34 | ||
| 30 | // Called when input devices should be loaded | ||
| 31 | void OnLoadInputDevices() override; | ||
| 32 | |||
| 33 | private: | 35 | private: |
| 34 | struct Buttons { | 36 | // This is nn::hid::detail::MouseLifo |
| 35 | union { | 37 | Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{}; |
| 36 | u32_le raw{}; | 38 | static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); |
| 37 | BitField<0, 1, u32> left; | 39 | Core::HID::MouseState next_state{}; |
| 38 | BitField<1, 1, u32> right; | ||
| 39 | BitField<2, 1, u32> middle; | ||
| 40 | BitField<3, 1, u32> forward; | ||
| 41 | BitField<4, 1, u32> back; | ||
| 42 | }; | ||
| 43 | }; | ||
| 44 | static_assert(sizeof(Buttons) == 0x4, "Buttons is an invalid size"); | ||
| 45 | |||
| 46 | struct Attributes { | ||
| 47 | union { | ||
| 48 | u32_le raw{}; | ||
| 49 | BitField<0, 1, u32> transferable; | ||
| 50 | BitField<1, 1, u32> is_connected; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); | ||
| 54 | |||
| 55 | struct MouseState { | ||
| 56 | s64_le sampling_number; | ||
| 57 | s64_le sampling_number2; | ||
| 58 | s32_le x; | ||
| 59 | s32_le y; | ||
| 60 | s32_le delta_x; | ||
| 61 | s32_le delta_y; | ||
| 62 | s32_le mouse_wheel_x; | ||
| 63 | s32_le mouse_wheel_y; | ||
| 64 | Buttons button; | ||
| 65 | Attributes attribute; | ||
| 66 | }; | ||
| 67 | static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size"); | ||
| 68 | |||
| 69 | struct SharedMemory { | ||
| 70 | CommonHeader header; | ||
| 71 | std::array<MouseState, 17> mouse_states; | ||
| 72 | }; | ||
| 73 | SharedMemory shared_memory{}; | ||
| 74 | 40 | ||
| 75 | std::unique_ptr<Input::MouseDevice> mouse_device; | 41 | Core::HID::AnalogStickState last_mouse_wheel_state; |
| 76 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> | 42 | Core::HID::EmulatedDevices* emulated_devices; |
| 77 | mouse_button_devices; | ||
| 78 | }; | 43 | }; |
| 79 | } // namespace Service::HID | 44 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 196876810..2705e9dcb 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -10,9 +10,9 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/settings.h" | 12 | #include "common/settings.h" |
| 13 | #include "core/core.h" | ||
| 14 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 15 | #include "core/frontend/input.h" | 14 | #include "core/hid/emulated_controller.h" |
| 15 | #include "core/hid/hid_core.h" | ||
| 16 | #include "core/hle/kernel/k_event.h" | 16 | #include "core/hle/kernel/k_event.h" |
| 17 | #include "core/hle/kernel/k_readable_event.h" | 17 | #include "core/hle/kernel/k_readable_event.h" |
| 18 | #include "core/hle/kernel/k_writable_event.h" | 18 | #include "core/hle/kernel/k_writable_event.h" |
| @@ -20,120 +20,26 @@ | |||
| 20 | #include "core/hle/service/kernel_helpers.h" | 20 | #include "core/hle/service/kernel_helpers.h" |
| 21 | 21 | ||
| 22 | namespace Service::HID { | 22 | namespace Service::HID { |
| 23 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | ||
| 24 | constexpr s32 HID_TRIGGER_MAX = 0x7fff; | ||
| 25 | [[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | ||
| 26 | constexpr std::size_t NPAD_OFFSET = 0x9A00; | 23 | constexpr std::size_t NPAD_OFFSET = 0x9A00; |
| 27 | constexpr u32 BATTERY_FULL = 2; | 24 | constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{ |
| 28 | constexpr u32 MAX_NPAD_ID = 7; | 25 | Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3, |
| 29 | constexpr std::size_t HANDHELD_INDEX = 8; | 26 | Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6, |
| 30 | constexpr std::array<u32, 10> npad_id_list{ | 27 | Core::HID::NpadIdType::Player7, Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other, |
| 31 | 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, | 28 | Core::HID::NpadIdType::Handheld, |
| 32 | }; | 29 | }; |
| 33 | 30 | ||
| 34 | enum class JoystickId : std::size_t { | 31 | bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { |
| 35 | Joystick_Left, | ||
| 36 | Joystick_Right, | ||
| 37 | }; | ||
| 38 | |||
| 39 | Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad( | ||
| 40 | Settings::ControllerType type) { | ||
| 41 | switch (type) { | ||
| 42 | case Settings::ControllerType::ProController: | ||
| 43 | return NPadControllerType::ProController; | ||
| 44 | case Settings::ControllerType::DualJoyconDetached: | ||
| 45 | return NPadControllerType::JoyDual; | ||
| 46 | case Settings::ControllerType::LeftJoycon: | ||
| 47 | return NPadControllerType::JoyLeft; | ||
| 48 | case Settings::ControllerType::RightJoycon: | ||
| 49 | return NPadControllerType::JoyRight; | ||
| 50 | case Settings::ControllerType::Handheld: | ||
| 51 | return NPadControllerType::Handheld; | ||
| 52 | case Settings::ControllerType::GameCube: | ||
| 53 | return NPadControllerType::GameCube; | ||
| 54 | default: | ||
| 55 | UNREACHABLE(); | ||
| 56 | return NPadControllerType::ProController; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | Settings::ControllerType Controller_NPad::MapNPadToSettingsType( | ||
| 61 | Controller_NPad::NPadControllerType type) { | ||
| 62 | switch (type) { | ||
| 63 | case NPadControllerType::ProController: | ||
| 64 | return Settings::ControllerType::ProController; | ||
| 65 | case NPadControllerType::JoyDual: | ||
| 66 | return Settings::ControllerType::DualJoyconDetached; | ||
| 67 | case NPadControllerType::JoyLeft: | ||
| 68 | return Settings::ControllerType::LeftJoycon; | ||
| 69 | case NPadControllerType::JoyRight: | ||
| 70 | return Settings::ControllerType::RightJoycon; | ||
| 71 | case NPadControllerType::Handheld: | ||
| 72 | return Settings::ControllerType::Handheld; | ||
| 73 | case NPadControllerType::GameCube: | ||
| 74 | return Settings::ControllerType::GameCube; | ||
| 75 | default: | ||
| 76 | UNREACHABLE(); | ||
| 77 | return Settings::ControllerType::ProController; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) { | ||
| 82 | switch (npad_id) { | 32 | switch (npad_id) { |
| 83 | case 0: | 33 | case Core::HID::NpadIdType::Player1: |
| 84 | case 1: | 34 | case Core::HID::NpadIdType::Player2: |
| 85 | case 2: | 35 | case Core::HID::NpadIdType::Player3: |
| 86 | case 3: | 36 | case Core::HID::NpadIdType::Player4: |
| 87 | case 4: | 37 | case Core::HID::NpadIdType::Player5: |
| 88 | case 5: | 38 | case Core::HID::NpadIdType::Player6: |
| 89 | case 6: | 39 | case Core::HID::NpadIdType::Player7: |
| 90 | case 7: | 40 | case Core::HID::NpadIdType::Player8: |
| 91 | return npad_id; | 41 | case Core::HID::NpadIdType::Other: |
| 92 | case HANDHELD_INDEX: | 42 | case Core::HID::NpadIdType::Handheld: |
| 93 | case NPAD_HANDHELD: | ||
| 94 | return HANDHELD_INDEX; | ||
| 95 | case 9: | ||
| 96 | case NPAD_UNKNOWN: | ||
| 97 | return 9; | ||
| 98 | default: | ||
| 99 | UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id); | ||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | u32 Controller_NPad::IndexToNPad(std::size_t index) { | ||
| 105 | switch (index) { | ||
| 106 | case 0: | ||
| 107 | case 1: | ||
| 108 | case 2: | ||
| 109 | case 3: | ||
| 110 | case 4: | ||
| 111 | case 5: | ||
| 112 | case 6: | ||
| 113 | case 7: | ||
| 114 | return static_cast<u32>(index); | ||
| 115 | case HANDHELD_INDEX: | ||
| 116 | return NPAD_HANDHELD; | ||
| 117 | case 9: | ||
| 118 | return NPAD_UNKNOWN; | ||
| 119 | default: | ||
| 120 | UNIMPLEMENTED_MSG("Unknown npad index {}", index); | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | bool Controller_NPad::IsNpadIdValid(u32 npad_id) { | ||
| 126 | switch (npad_id) { | ||
| 127 | case 0: | ||
| 128 | case 1: | ||
| 129 | case 2: | ||
| 130 | case 3: | ||
| 131 | case 4: | ||
| 132 | case 5: | ||
| 133 | case 6: | ||
| 134 | case 7: | ||
| 135 | case NPAD_UNKNOWN: | ||
| 136 | case NPAD_HANDHELD: | ||
| 137 | return true; | 43 | return true; |
| 138 | default: | 44 | default: |
| 139 | LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id); | 45 | LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id); |
| @@ -141,305 +47,337 @@ bool Controller_NPad::IsNpadIdValid(u32 npad_id) { | |||
| 141 | } | 47 | } |
| 142 | } | 48 | } |
| 143 | 49 | ||
| 144 | bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) { | 50 | bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { |
| 145 | return IsNpadIdValid(device_handle.npad_id) && | 51 | return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && |
| 146 | device_handle.npad_type < NpadType::MaxNpadType && | 52 | device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && |
| 147 | device_handle.device_index < DeviceIndex::MaxDeviceIndex; | 53 | device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; |
| 148 | } | 54 | } |
| 149 | 55 | ||
| 150 | Controller_NPad::Controller_NPad(Core::System& system_, | 56 | bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { |
| 57 | return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && | ||
| 58 | device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && | ||
| 59 | device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; | ||
| 60 | } | ||
| 61 | |||
| 62 | Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, | ||
| 151 | KernelHelpers::ServiceContext& service_context_) | 63 | KernelHelpers::ServiceContext& service_context_) |
| 152 | : ControllerBase{system_}, service_context{service_context_} { | 64 | : ControllerBase{hid_core_}, service_context{service_context_} { |
| 153 | latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}); | 65 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 66 | auto& controller = controller_data[i]; | ||
| 67 | controller.device = hid_core.GetEmulatedControllerByIndex(i); | ||
| 68 | controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = | ||
| 69 | DEFAULT_VIBRATION_VALUE; | ||
| 70 | controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value = | ||
| 71 | DEFAULT_VIBRATION_VALUE; | ||
| 72 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 73 | .on_change = [this, | ||
| 74 | i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); }, | ||
| 75 | .is_npad_service = true, | ||
| 76 | }; | ||
| 77 | controller.callback_key = controller.device->SetCallback(engine_callback); | ||
| 78 | } | ||
| 154 | } | 79 | } |
| 155 | 80 | ||
| 156 | Controller_NPad::~Controller_NPad() { | 81 | Controller_NPad::~Controller_NPad() { |
| 82 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | ||
| 83 | auto& controller = controller_data[i]; | ||
| 84 | controller.device->DeleteCallback(controller.callback_key); | ||
| 85 | } | ||
| 157 | OnRelease(); | 86 | OnRelease(); |
| 158 | } | 87 | } |
| 159 | 88 | ||
| 160 | void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { | 89 | void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, |
| 161 | const auto controller_type = connected_controllers[controller_idx].type; | 90 | std::size_t controller_idx) { |
| 162 | auto& controller = shared_memory_entries[controller_idx]; | 91 | if (type == Core::HID::ControllerTriggerType::All) { |
| 163 | if (controller_type == NPadControllerType::None) { | 92 | ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); |
| 164 | styleset_changed_events[controller_idx]->GetWritableEvent().Signal(); | 93 | ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); |
| 94 | return; | ||
| 95 | } | ||
| 96 | if (controller_idx >= controller_data.size()) { | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 100 | auto& controller = controller_data[controller_idx]; | ||
| 101 | const auto is_connected = controller.device->IsConnected(); | ||
| 102 | const auto npad_type = controller.device->GetNpadStyleIndex(); | ||
| 103 | const auto npad_id = controller.device->GetNpadIdType(); | ||
| 104 | switch (type) { | ||
| 105 | case Core::HID::ControllerTriggerType::Connected: | ||
| 106 | case Core::HID::ControllerTriggerType::Disconnected: | ||
| 107 | if (is_connected == controller.is_connected) { | ||
| 108 | return; | ||
| 109 | } | ||
| 110 | UpdateControllerAt(npad_type, npad_id, is_connected); | ||
| 111 | break; | ||
| 112 | case Core::HID::ControllerTriggerType::Battery: { | ||
| 113 | if (!controller.device->IsConnected()) { | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | auto& shared_memory = controller.shared_memory_entry; | ||
| 117 | const auto& battery_level = controller.device->GetBattery(); | ||
| 118 | shared_memory.battery_level_dual = battery_level.dual.battery_level; | ||
| 119 | shared_memory.battery_level_left = battery_level.left.battery_level; | ||
| 120 | shared_memory.battery_level_right = battery_level.right.battery_level; | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | default: | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | ||
| 129 | auto& controller = GetControllerFromNpadIdType(npad_id); | ||
| 130 | if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { | ||
| 165 | return; | 131 | return; |
| 166 | } | 132 | } |
| 167 | controller.style_set.raw = 0; // Zero out | 133 | LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); |
| 168 | controller.device_type.raw = 0; | 134 | const auto controller_type = controller.device->GetNpadStyleIndex(); |
| 169 | controller.system_properties.raw = 0; | 135 | auto& shared_memory = controller.shared_memory_entry; |
| 136 | if (controller_type == Core::HID::NpadStyleIndex::None) { | ||
| 137 | controller.styleset_changed_event->GetWritableEvent().Signal(); | ||
| 138 | return; | ||
| 139 | } | ||
| 140 | shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None; | ||
| 141 | shared_memory.device_type.raw = 0; | ||
| 142 | shared_memory.system_properties.raw = 0; | ||
| 170 | switch (controller_type) { | 143 | switch (controller_type) { |
| 171 | case NPadControllerType::None: | 144 | case Core::HID::NpadStyleIndex::None: |
| 172 | UNREACHABLE(); | 145 | UNREACHABLE(); |
| 173 | break; | 146 | break; |
| 174 | case NPadControllerType::ProController: | 147 | case Core::HID::NpadStyleIndex::ProController: |
| 175 | controller.style_set.fullkey.Assign(1); | 148 | shared_memory.style_tag.fullkey.Assign(1); |
| 176 | controller.device_type.fullkey.Assign(1); | 149 | shared_memory.device_type.fullkey.Assign(1); |
| 177 | controller.system_properties.is_vertical.Assign(1); | 150 | shared_memory.system_properties.is_vertical.Assign(1); |
| 178 | controller.system_properties.use_plus.Assign(1); | 151 | shared_memory.system_properties.use_plus.Assign(1); |
| 179 | controller.system_properties.use_minus.Assign(1); | 152 | shared_memory.system_properties.use_minus.Assign(1); |
| 180 | controller.assignment_mode = NpadAssignments::Single; | 153 | shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController; |
| 181 | controller.footer_type = AppletFooterUiType::SwitchProController; | ||
| 182 | break; | 154 | break; |
| 183 | case NPadControllerType::Handheld: | 155 | case Core::HID::NpadStyleIndex::Handheld: |
| 184 | controller.style_set.handheld.Assign(1); | 156 | shared_memory.style_tag.handheld.Assign(1); |
| 185 | controller.device_type.handheld_left.Assign(1); | 157 | shared_memory.device_type.handheld_left.Assign(1); |
| 186 | controller.device_type.handheld_right.Assign(1); | 158 | shared_memory.device_type.handheld_right.Assign(1); |
| 187 | controller.system_properties.is_vertical.Assign(1); | 159 | shared_memory.system_properties.is_vertical.Assign(1); |
| 188 | controller.system_properties.use_plus.Assign(1); | 160 | shared_memory.system_properties.use_plus.Assign(1); |
| 189 | controller.system_properties.use_minus.Assign(1); | 161 | shared_memory.system_properties.use_minus.Assign(1); |
| 190 | controller.assignment_mode = NpadAssignments::Dual; | 162 | shared_memory.system_properties.use_directional_buttons.Assign(1); |
| 191 | controller.footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; | 163 | shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual; |
| 164 | shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; | ||
| 165 | break; | ||
| 166 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 167 | shared_memory.style_tag.joycon_dual.Assign(1); | ||
| 168 | if (controller.is_dual_left_connected) { | ||
| 169 | shared_memory.device_type.joycon_left.Assign(1); | ||
| 170 | shared_memory.system_properties.use_minus.Assign(1); | ||
| 171 | } | ||
| 172 | if (controller.is_dual_right_connected) { | ||
| 173 | shared_memory.device_type.joycon_right.Assign(1); | ||
| 174 | shared_memory.system_properties.use_plus.Assign(1); | ||
| 175 | } | ||
| 176 | shared_memory.system_properties.use_directional_buttons.Assign(1); | ||
| 177 | shared_memory.system_properties.is_vertical.Assign(1); | ||
| 178 | shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual; | ||
| 179 | if (controller.is_dual_left_connected && controller.is_dual_right_connected) { | ||
| 180 | shared_memory.applet_footer.type = AppletFooterUiType::JoyDual; | ||
| 181 | } else if (controller.is_dual_left_connected) { | ||
| 182 | shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; | ||
| 183 | } else { | ||
| 184 | shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; | ||
| 185 | } | ||
| 192 | break; | 186 | break; |
| 193 | case NPadControllerType::JoyDual: | 187 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 194 | controller.style_set.joycon_dual.Assign(1); | 188 | shared_memory.style_tag.joycon_left.Assign(1); |
| 195 | controller.device_type.joycon_left.Assign(1); | 189 | shared_memory.device_type.joycon_left.Assign(1); |
| 196 | controller.device_type.joycon_right.Assign(1); | 190 | shared_memory.system_properties.is_horizontal.Assign(1); |
| 197 | controller.system_properties.is_vertical.Assign(1); | 191 | shared_memory.system_properties.use_minus.Assign(1); |
| 198 | controller.system_properties.use_plus.Assign(1); | 192 | shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; |
| 199 | controller.system_properties.use_minus.Assign(1); | ||
| 200 | controller.assignment_mode = NpadAssignments::Dual; | ||
| 201 | controller.footer_type = AppletFooterUiType::JoyDual; | ||
| 202 | break; | 193 | break; |
| 203 | case NPadControllerType::JoyLeft: | 194 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 204 | controller.style_set.joycon_left.Assign(1); | 195 | shared_memory.style_tag.joycon_right.Assign(1); |
| 205 | controller.device_type.joycon_left.Assign(1); | 196 | shared_memory.device_type.joycon_right.Assign(1); |
| 206 | controller.system_properties.is_horizontal.Assign(1); | 197 | shared_memory.system_properties.is_horizontal.Assign(1); |
| 207 | controller.system_properties.use_minus.Assign(1); | 198 | shared_memory.system_properties.use_plus.Assign(1); |
| 208 | controller.assignment_mode = NpadAssignments::Single; | 199 | shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; |
| 209 | controller.footer_type = AppletFooterUiType::JoyLeftHorizontal; | ||
| 210 | break; | 200 | break; |
| 211 | case NPadControllerType::JoyRight: | 201 | case Core::HID::NpadStyleIndex::GameCube: |
| 212 | controller.style_set.joycon_right.Assign(1); | 202 | shared_memory.style_tag.gamecube.Assign(1); |
| 213 | controller.device_type.joycon_right.Assign(1); | 203 | shared_memory.device_type.fullkey.Assign(1); |
| 214 | controller.system_properties.is_horizontal.Assign(1); | 204 | shared_memory.system_properties.is_vertical.Assign(1); |
| 215 | controller.system_properties.use_plus.Assign(1); | 205 | shared_memory.system_properties.use_plus.Assign(1); |
| 216 | controller.assignment_mode = NpadAssignments::Single; | ||
| 217 | controller.footer_type = AppletFooterUiType::JoyRightHorizontal; | ||
| 218 | break; | 206 | break; |
| 219 | case NPadControllerType::GameCube: | 207 | case Core::HID::NpadStyleIndex::Pokeball: |
| 220 | controller.style_set.gamecube.Assign(1); | 208 | shared_memory.style_tag.palma.Assign(1); |
| 221 | // The GC Controller behaves like a wired Pro Controller | 209 | shared_memory.device_type.palma.Assign(1); |
| 222 | controller.device_type.fullkey.Assign(1); | ||
| 223 | controller.system_properties.is_vertical.Assign(1); | ||
| 224 | controller.system_properties.use_plus.Assign(1); | ||
| 225 | break; | 210 | break; |
| 226 | case NPadControllerType::Pokeball: | 211 | case Core::HID::NpadStyleIndex::NES: |
| 227 | controller.style_set.palma.Assign(1); | 212 | shared_memory.style_tag.lark.Assign(1); |
| 228 | controller.device_type.palma.Assign(1); | 213 | shared_memory.device_type.fullkey.Assign(1); |
| 229 | controller.assignment_mode = NpadAssignments::Single; | 214 | break; |
| 215 | case Core::HID::NpadStyleIndex::SNES: | ||
| 216 | shared_memory.style_tag.lucia.Assign(1); | ||
| 217 | shared_memory.device_type.fullkey.Assign(1); | ||
| 218 | shared_memory.applet_footer.type = AppletFooterUiType::Lucia; | ||
| 219 | break; | ||
| 220 | case Core::HID::NpadStyleIndex::N64: | ||
| 221 | shared_memory.style_tag.lagoon.Assign(1); | ||
| 222 | shared_memory.device_type.fullkey.Assign(1); | ||
| 223 | shared_memory.applet_footer.type = AppletFooterUiType::Lagon; | ||
| 224 | break; | ||
| 225 | case Core::HID::NpadStyleIndex::SegaGenesis: | ||
| 226 | shared_memory.style_tag.lager.Assign(1); | ||
| 227 | shared_memory.device_type.fullkey.Assign(1); | ||
| 228 | break; | ||
| 229 | default: | ||
| 230 | break; | 230 | break; |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | controller.fullkey_color.attribute = ColorAttributes::Ok; | 233 | const auto& body_colors = controller.device->GetColors(); |
| 234 | controller.fullkey_color.fullkey.body = 0; | ||
| 235 | controller.fullkey_color.fullkey.button = 0; | ||
| 236 | 234 | ||
| 237 | controller.joycon_color.attribute = ColorAttributes::Ok; | 235 | shared_memory.fullkey_color.attribute = ColorAttribute::Ok; |
| 238 | controller.joycon_color.left.body = | 236 | shared_memory.fullkey_color.fullkey = body_colors.fullkey; |
| 239 | Settings::values.players.GetValue()[controller_idx].body_color_left; | ||
| 240 | controller.joycon_color.left.button = | ||
| 241 | Settings::values.players.GetValue()[controller_idx].button_color_left; | ||
| 242 | controller.joycon_color.right.body = | ||
| 243 | Settings::values.players.GetValue()[controller_idx].body_color_right; | ||
| 244 | controller.joycon_color.right.button = | ||
| 245 | Settings::values.players.GetValue()[controller_idx].button_color_right; | ||
| 246 | 237 | ||
| 247 | // TODO: Investigate when we should report all batery types | 238 | shared_memory.joycon_color.attribute = ColorAttribute::Ok; |
| 248 | controller.battery_level_dual = BATTERY_FULL; | 239 | shared_memory.joycon_color.left = body_colors.left; |
| 249 | controller.battery_level_left = BATTERY_FULL; | 240 | shared_memory.joycon_color.right = body_colors.right; |
| 250 | controller.battery_level_right = BATTERY_FULL; | ||
| 251 | 241 | ||
| 252 | SignalStyleSetChangedEvent(IndexToNPad(controller_idx)); | 242 | // TODO: Investigate when we should report all batery types |
| 243 | const auto& battery_level = controller.device->GetBattery(); | ||
| 244 | shared_memory.battery_level_dual = battery_level.dual.battery_level; | ||
| 245 | shared_memory.battery_level_left = battery_level.left.battery_level; | ||
| 246 | shared_memory.battery_level_right = battery_level.right.battery_level; | ||
| 247 | |||
| 248 | controller.is_connected = true; | ||
| 249 | controller.device->Connect(); | ||
| 250 | SignalStyleSetChangedEvent(npad_id); | ||
| 251 | WriteEmptyEntry(controller.shared_memory_entry); | ||
| 253 | } | 252 | } |
| 254 | 253 | ||
| 255 | void Controller_NPad::OnInit() { | 254 | void Controller_NPad::OnInit() { |
| 256 | for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { | ||
| 257 | styleset_changed_events[i] = | ||
| 258 | service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); | ||
| 259 | } | ||
| 260 | |||
| 261 | if (!IsControllerActivated()) { | 255 | if (!IsControllerActivated()) { |
| 262 | return; | 256 | return; |
| 263 | } | 257 | } |
| 264 | 258 | ||
| 265 | OnLoadInputDevices(); | 259 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 266 | 260 | auto& controller = controller_data[i]; | |
| 267 | if (style.raw == 0) { | 261 | controller.styleset_changed_event = |
| 268 | // We want to support all controllers | 262 | service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); |
| 269 | style.handheld.Assign(1); | ||
| 270 | style.joycon_left.Assign(1); | ||
| 271 | style.joycon_right.Assign(1); | ||
| 272 | style.joycon_dual.Assign(1); | ||
| 273 | style.fullkey.Assign(1); | ||
| 274 | style.gamecube.Assign(1); | ||
| 275 | style.palma.Assign(1); | ||
| 276 | } | ||
| 277 | |||
| 278 | std::transform(Settings::values.players.GetValue().begin(), | ||
| 279 | Settings::values.players.GetValue().end(), connected_controllers.begin(), | ||
| 280 | [](const Settings::PlayerInput& player) { | ||
| 281 | return ControllerHolder{MapSettingsTypeToNPad(player.controller_type), | ||
| 282 | player.connected}; | ||
| 283 | }); | ||
| 284 | |||
| 285 | // Connect the Player 1 or Handheld controller if none are connected. | ||
| 286 | if (std::none_of(connected_controllers.begin(), connected_controllers.end(), | ||
| 287 | [](const ControllerHolder& controller) { return controller.is_connected; })) { | ||
| 288 | const auto controller = | ||
| 289 | MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type); | ||
| 290 | if (controller == NPadControllerType::Handheld) { | ||
| 291 | Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true; | ||
| 292 | connected_controllers[HANDHELD_INDEX] = {controller, true}; | ||
| 293 | } else { | ||
| 294 | Settings::values.players.GetValue()[0].connected = true; | ||
| 295 | connected_controllers[0] = {controller, true}; | ||
| 296 | } | ||
| 297 | } | 263 | } |
| 298 | 264 | ||
| 299 | // Account for handheld | 265 | if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) { |
| 300 | if (connected_controllers[HANDHELD_INDEX].is_connected) { | 266 | // We want to support all controllers |
| 301 | connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; | 267 | hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); |
| 302 | } | 268 | } |
| 303 | 269 | ||
| 304 | supported_npad_id_types.resize(npad_id_list.size()); | 270 | supported_npad_id_types.resize(npad_id_list.size()); |
| 305 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), | 271 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), |
| 306 | npad_id_list.size() * sizeof(u32)); | 272 | npad_id_list.size() * sizeof(Core::HID::NpadIdType)); |
| 273 | |||
| 274 | // Prefill controller buffers | ||
| 275 | for (auto& controller : controller_data) { | ||
| 276 | auto& npad = controller.shared_memory_entry; | ||
| 277 | npad.fullkey_color = { | ||
| 278 | .attribute = ColorAttribute::NoController, | ||
| 279 | .fullkey = {}, | ||
| 280 | }; | ||
| 281 | npad.joycon_color = { | ||
| 282 | .attribute = ColorAttribute::NoController, | ||
| 283 | .left = {}, | ||
| 284 | .right = {}, | ||
| 285 | }; | ||
| 286 | // HW seems to initialize the first 19 entries | ||
| 287 | for (std::size_t i = 0; i < 19; ++i) { | ||
| 288 | WriteEmptyEntry(npad); | ||
| 289 | } | ||
| 290 | } | ||
| 307 | 291 | ||
| 308 | for (std::size_t i = 0; i < connected_controllers.size(); ++i) { | 292 | // Connect controllers |
| 309 | const auto& controller = connected_controllers[i]; | 293 | for (auto& controller : controller_data) { |
| 310 | if (controller.is_connected) { | 294 | const auto& device = controller.device; |
| 311 | AddNewControllerAt(controller.type, i); | 295 | if (device->IsConnected()) { |
| 296 | AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType()); | ||
| 312 | } | 297 | } |
| 313 | } | 298 | } |
| 314 | } | 299 | } |
| 315 | 300 | ||
| 316 | void Controller_NPad::OnLoadInputDevices() { | 301 | void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { |
| 317 | const auto& players = Settings::values.players.GetValue(); | 302 | NPadGenericState dummy_pad_state{}; |
| 303 | NpadGcTriggerState dummy_gc_state{}; | ||
| 304 | dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 305 | npad.fullkey_lifo.WriteNextEntry(dummy_pad_state); | ||
| 306 | dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 307 | npad.handheld_lifo.WriteNextEntry(dummy_pad_state); | ||
| 308 | dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 309 | npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state); | ||
| 310 | dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 311 | npad.joy_left_lifo.WriteNextEntry(dummy_pad_state); | ||
| 312 | dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 313 | npad.joy_right_lifo.WriteNextEntry(dummy_pad_state); | ||
| 314 | dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 315 | npad.palma_lifo.WriteNextEntry(dummy_pad_state); | ||
| 316 | dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 317 | npad.system_ext_lifo.WriteNextEntry(dummy_pad_state); | ||
| 318 | dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 319 | npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state); | ||
| 320 | } | ||
| 318 | 321 | ||
| 319 | std::lock_guard lock{mutex}; | 322 | void Controller_NPad::OnRelease() { |
| 320 | for (std::size_t i = 0; i < players.size(); ++i) { | 323 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 321 | std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, | 324 | auto& controller = controller_data[i]; |
| 322 | players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, | 325 | service_context.CloseEvent(controller.styleset_changed_event); |
| 323 | buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>); | 326 | for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) { |
| 324 | std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, | 327 | VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {}); |
| 325 | players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, | ||
| 326 | sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); | ||
| 327 | std::transform(players[i].vibrations.begin() + | ||
| 328 | Settings::NativeVibration::VIBRATION_HID_BEGIN, | ||
| 329 | players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END, | ||
| 330 | vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>); | ||
| 331 | std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, | ||
| 332 | players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, | ||
| 333 | motions[i].begin(), Input::CreateDevice<Input::MotionDevice>); | ||
| 334 | for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) { | ||
| 335 | InitializeVibrationDeviceAtIndex(i, device_idx); | ||
| 336 | } | 328 | } |
| 337 | } | 329 | } |
| 338 | } | 330 | } |
| 339 | 331 | ||
| 340 | void Controller_NPad::OnRelease() { | 332 | void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { |
| 341 | for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) { | 333 | std::lock_guard lock{mutex}; |
| 342 | for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) { | 334 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 343 | VibrateControllerAtIndex(npad_idx, device_idx, {}); | 335 | const auto controller_type = controller.device->GetNpadStyleIndex(); |
| 344 | } | 336 | if (!controller.device->IsConnected()) { |
| 337 | return; | ||
| 345 | } | 338 | } |
| 346 | 339 | ||
| 347 | for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { | 340 | auto& pad_entry = controller.npad_pad_state; |
| 348 | service_context.CloseEvent(styleset_changed_events[i]); | 341 | auto& trigger_entry = controller.npad_trigger_state; |
| 342 | const auto button_state = controller.device->GetNpadButtons(); | ||
| 343 | const auto stick_state = controller.device->GetSticks(); | ||
| 344 | |||
| 345 | using btn = Core::HID::NpadButton; | ||
| 346 | pad_entry.npad_buttons.raw = btn::None; | ||
| 347 | if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) { | ||
| 348 | constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R | | ||
| 349 | btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp | | ||
| 350 | btn::StickRRight | btn::StickRDown; | ||
| 351 | pad_entry.npad_buttons.raw = button_state.raw & right_button_mask; | ||
| 352 | pad_entry.r_stick = stick_state.right; | ||
| 349 | } | 353 | } |
| 350 | } | ||
| 351 | 354 | ||
| 352 | void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | 355 | if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) { |
| 353 | std::lock_guard lock{mutex}; | 356 | constexpr btn left_button_mask = |
| 357 | btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL | | ||
| 358 | btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown; | ||
| 359 | pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask; | ||
| 360 | pad_entry.l_stick = stick_state.left; | ||
| 361 | } | ||
| 354 | 362 | ||
| 355 | const auto controller_idx = NPadIdToIndex(npad_id); | 363 | if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) { |
| 356 | const auto controller_type = connected_controllers[controller_idx].type; | 364 | pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); |
| 357 | if (!connected_controllers[controller_idx].is_connected) { | 365 | pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); |
| 358 | return; | ||
| 359 | } | 366 | } |
| 360 | auto& pad_state = npad_pad_states[controller_idx].pad_states; | 367 | |
| 361 | auto& lstick_entry = npad_pad_states[controller_idx].l_stick; | 368 | if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) { |
| 362 | auto& rstick_entry = npad_pad_states[controller_idx].r_stick; | 369 | pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); |
| 363 | auto& trigger_entry = npad_trigger_states[controller_idx]; | 370 | pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); |
| 364 | const auto& button_state = buttons[controller_idx]; | 371 | } |
| 365 | const auto& analog_state = sticks[controller_idx]; | 372 | |
| 366 | const auto [stick_l_x_f, stick_l_y_f] = | 373 | if (controller_type == Core::HID::NpadStyleIndex::GameCube) { |
| 367 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | 374 | const auto& trigger_state = controller.device->GetTriggers(); |
| 368 | const auto [stick_r_x_f, stick_r_y_f] = | 375 | trigger_entry.l_analog = trigger_state.left; |
| 369 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | 376 | trigger_entry.r_analog = trigger_state.right; |
| 370 | 377 | pad_entry.npad_buttons.zl.Assign(false); | |
| 371 | using namespace Settings::NativeButton; | 378 | pad_entry.npad_buttons.zr.Assign(button_state.r); |
| 372 | if (controller_type != NPadControllerType::JoyLeft) { | 379 | pad_entry.npad_buttons.l.Assign(button_state.zl); |
| 373 | pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); | 380 | pad_entry.npad_buttons.r.Assign(button_state.zr); |
| 374 | pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 375 | pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 376 | pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 377 | pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 378 | pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 379 | pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 380 | pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 381 | |||
| 382 | pad_state.r_stick_right.Assign( | ||
| 383 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 384 | ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); | ||
| 385 | pad_state.r_stick_left.Assign( | ||
| 386 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 387 | ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); | ||
| 388 | pad_state.r_stick_up.Assign( | ||
| 389 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 390 | ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); | ||
| 391 | pad_state.r_stick_down.Assign( | ||
| 392 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 393 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); | ||
| 394 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | ||
| 395 | rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | ||
| 396 | } | ||
| 397 | |||
| 398 | if (controller_type != NPadControllerType::JoyRight) { | ||
| 399 | pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 400 | pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 401 | pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 402 | pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 403 | pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 404 | pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 405 | pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 406 | pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 407 | |||
| 408 | pad_state.l_stick_right.Assign( | ||
| 409 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] | ||
| 410 | ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); | ||
| 411 | pad_state.l_stick_left.Assign( | ||
| 412 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] | ||
| 413 | ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); | ||
| 414 | pad_state.l_stick_up.Assign( | ||
| 415 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] | ||
| 416 | ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); | ||
| 417 | pad_state.l_stick_down.Assign( | ||
| 418 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] | ||
| 419 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); | ||
| 420 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | ||
| 421 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | ||
| 422 | } | ||
| 423 | |||
| 424 | if (controller_type == NPadControllerType::JoyLeft) { | ||
| 425 | pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 426 | pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 427 | } | ||
| 428 | |||
| 429 | if (controller_type == NPadControllerType::JoyRight) { | ||
| 430 | pad_state.right_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 431 | pad_state.right_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 432 | } | ||
| 433 | |||
| 434 | if (controller_type == NPadControllerType::GameCube) { | ||
| 435 | trigger_entry.l_analog = static_cast<s32>( | ||
| 436 | button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0); | ||
| 437 | trigger_entry.r_analog = static_cast<s32>( | ||
| 438 | button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0); | ||
| 439 | pad_state.zl.Assign(false); | ||
| 440 | pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 441 | pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 442 | pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 443 | } | 381 | } |
| 444 | } | 382 | } |
| 445 | 383 | ||
| @@ -448,173 +386,136 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 448 | if (!IsControllerActivated()) { | 386 | if (!IsControllerActivated()) { |
| 449 | return; | 387 | return; |
| 450 | } | 388 | } |
| 451 | for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) { | ||
| 452 | auto& npad = shared_memory_entries[i]; | ||
| 453 | const std::array<NPadGeneric*, 7> controller_npads{ | ||
| 454 | &npad.fullkey_states, &npad.handheld_states, &npad.joy_dual_states, | ||
| 455 | &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states, | ||
| 456 | &npad.system_ext_states}; | ||
| 457 | |||
| 458 | // There is the posibility to have more controllers with analog triggers | ||
| 459 | const std::array<TriggerGeneric*, 1> controller_triggers{ | ||
| 460 | &npad.gc_trigger_states, | ||
| 461 | }; | ||
| 462 | |||
| 463 | for (auto* main_controller : controller_npads) { | ||
| 464 | main_controller->common.entry_count = 16; | ||
| 465 | main_controller->common.total_entry_count = 17; | ||
| 466 | |||
| 467 | const auto& last_entry = | ||
| 468 | main_controller->npad[main_controller->common.last_entry_index]; | ||
| 469 | |||
| 470 | main_controller->common.timestamp = core_timing.GetCPUTicks(); | ||
| 471 | main_controller->common.last_entry_index = | ||
| 472 | (main_controller->common.last_entry_index + 1) % 17; | ||
| 473 | |||
| 474 | auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index]; | ||
| 475 | |||
| 476 | cur_entry.timestamp = last_entry.timestamp + 1; | ||
| 477 | cur_entry.timestamp2 = cur_entry.timestamp; | ||
| 478 | } | ||
| 479 | |||
| 480 | for (auto* analog_trigger : controller_triggers) { | ||
| 481 | analog_trigger->entry_count = 16; | ||
| 482 | analog_trigger->total_entry_count = 17; | ||
| 483 | |||
| 484 | const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index]; | ||
| 485 | |||
| 486 | analog_trigger->timestamp = core_timing.GetCPUTicks(); | ||
| 487 | analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17; | ||
| 488 | |||
| 489 | auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index]; | ||
| 490 | 389 | ||
| 491 | cur_entry.timestamp = last_entry.timestamp + 1; | 390 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 492 | cur_entry.timestamp2 = cur_entry.timestamp; | 391 | auto& controller = controller_data[i]; |
| 493 | } | 392 | auto& npad = controller.shared_memory_entry; |
| 494 | 393 | ||
| 495 | const auto& controller_type = connected_controllers[i].type; | 394 | const auto& controller_type = controller.device->GetNpadStyleIndex(); |
| 496 | 395 | ||
| 497 | if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { | 396 | if (controller_type == Core::HID::NpadStyleIndex::None || |
| 397 | !controller.device->IsConnected()) { | ||
| 398 | // Refresh shared memory | ||
| 399 | std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), | ||
| 400 | &controller.shared_memory_entry, sizeof(NpadInternalState)); | ||
| 498 | continue; | 401 | continue; |
| 499 | } | 402 | } |
| 500 | const u32 npad_index = static_cast<u32>(i); | ||
| 501 | |||
| 502 | RequestPadStateUpdate(npad_index); | ||
| 503 | auto& pad_state = npad_pad_states[npad_index]; | ||
| 504 | auto& trigger_state = npad_trigger_states[npad_index]; | ||
| 505 | |||
| 506 | auto& main_controller = | ||
| 507 | npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index]; | ||
| 508 | auto& handheld_entry = | ||
| 509 | npad.handheld_states.npad[npad.handheld_states.common.last_entry_index]; | ||
| 510 | auto& dual_entry = npad.joy_dual_states.npad[npad.joy_dual_states.common.last_entry_index]; | ||
| 511 | auto& left_entry = npad.joy_left_states.npad[npad.joy_left_states.common.last_entry_index]; | ||
| 512 | auto& right_entry = | ||
| 513 | npad.joy_right_states.npad[npad.joy_right_states.common.last_entry_index]; | ||
| 514 | auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index]; | ||
| 515 | auto& libnx_entry = | ||
| 516 | npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index]; | ||
| 517 | auto& trigger_entry = | ||
| 518 | npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index]; | ||
| 519 | |||
| 520 | libnx_entry.connection_status.raw = 0; | ||
| 521 | libnx_entry.connection_status.is_connected.Assign(1); | ||
| 522 | 403 | ||
| 404 | RequestPadStateUpdate(controller.device->GetNpadIdType()); | ||
| 405 | auto& pad_state = controller.npad_pad_state; | ||
| 406 | auto& libnx_state = controller.npad_libnx_state; | ||
| 407 | auto& trigger_state = controller.npad_trigger_state; | ||
| 408 | |||
| 409 | // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate | ||
| 410 | // any controllers. | ||
| 411 | libnx_state.connection_status.raw = 0; | ||
| 412 | libnx_state.connection_status.is_connected.Assign(1); | ||
| 523 | switch (controller_type) { | 413 | switch (controller_type) { |
| 524 | case NPadControllerType::None: | 414 | case Core::HID::NpadStyleIndex::None: |
| 525 | UNREACHABLE(); | 415 | UNREACHABLE(); |
| 526 | break; | 416 | break; |
| 527 | case NPadControllerType::ProController: | 417 | case Core::HID::NpadStyleIndex::ProController: |
| 528 | main_controller.connection_status.raw = 0; | 418 | case Core::HID::NpadStyleIndex::NES: |
| 529 | main_controller.connection_status.is_connected.Assign(1); | 419 | case Core::HID::NpadStyleIndex::SNES: |
| 530 | main_controller.connection_status.is_wired.Assign(1); | 420 | case Core::HID::NpadStyleIndex::N64: |
| 531 | main_controller.pad.pad_states.raw = pad_state.pad_states.raw; | 421 | case Core::HID::NpadStyleIndex::SegaGenesis: |
| 532 | main_controller.pad.l_stick = pad_state.l_stick; | 422 | pad_state.connection_status.raw = 0; |
| 533 | main_controller.pad.r_stick = pad_state.r_stick; | 423 | pad_state.connection_status.is_connected.Assign(1); |
| 534 | 424 | pad_state.connection_status.is_wired.Assign(1); | |
| 535 | libnx_entry.connection_status.is_wired.Assign(1); | 425 | |
| 426 | libnx_state.connection_status.is_wired.Assign(1); | ||
| 427 | pad_state.sampling_number = | ||
| 428 | npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 429 | npad.fullkey_lifo.WriteNextEntry(pad_state); | ||
| 536 | break; | 430 | break; |
| 537 | case NPadControllerType::Handheld: | 431 | case Core::HID::NpadStyleIndex::Handheld: |
| 538 | handheld_entry.connection_status.raw = 0; | 432 | pad_state.connection_status.raw = 0; |
| 539 | handheld_entry.connection_status.is_connected.Assign(1); | 433 | pad_state.connection_status.is_connected.Assign(1); |
| 540 | handheld_entry.connection_status.is_wired.Assign(1); | 434 | pad_state.connection_status.is_wired.Assign(1); |
| 541 | handheld_entry.connection_status.is_left_connected.Assign(1); | 435 | pad_state.connection_status.is_left_connected.Assign(1); |
| 542 | handheld_entry.connection_status.is_right_connected.Assign(1); | 436 | pad_state.connection_status.is_right_connected.Assign(1); |
| 543 | handheld_entry.connection_status.is_left_wired.Assign(1); | 437 | pad_state.connection_status.is_left_wired.Assign(1); |
| 544 | handheld_entry.connection_status.is_right_wired.Assign(1); | 438 | pad_state.connection_status.is_right_wired.Assign(1); |
| 545 | handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 439 | |
| 546 | handheld_entry.pad.l_stick = pad_state.l_stick; | 440 | libnx_state.connection_status.is_wired.Assign(1); |
| 547 | handheld_entry.pad.r_stick = pad_state.r_stick; | 441 | libnx_state.connection_status.is_left_connected.Assign(1); |
| 548 | 442 | libnx_state.connection_status.is_right_connected.Assign(1); | |
| 549 | libnx_entry.connection_status.is_wired.Assign(1); | 443 | libnx_state.connection_status.is_left_wired.Assign(1); |
| 550 | libnx_entry.connection_status.is_left_connected.Assign(1); | 444 | libnx_state.connection_status.is_right_wired.Assign(1); |
| 551 | libnx_entry.connection_status.is_right_connected.Assign(1); | 445 | pad_state.sampling_number = |
| 552 | libnx_entry.connection_status.is_left_wired.Assign(1); | 446 | npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; |
| 553 | libnx_entry.connection_status.is_right_wired.Assign(1); | 447 | npad.handheld_lifo.WriteNextEntry(pad_state); |
| 554 | break; | 448 | break; |
| 555 | case NPadControllerType::JoyDual: | 449 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 556 | dual_entry.connection_status.raw = 0; | 450 | pad_state.connection_status.raw = 0; |
| 557 | dual_entry.connection_status.is_connected.Assign(1); | 451 | pad_state.connection_status.is_connected.Assign(1); |
| 558 | dual_entry.connection_status.is_left_connected.Assign(1); | 452 | if (controller.is_dual_left_connected) { |
| 559 | dual_entry.connection_status.is_right_connected.Assign(1); | 453 | pad_state.connection_status.is_left_connected.Assign(1); |
| 560 | dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 454 | libnx_state.connection_status.is_left_connected.Assign(1); |
| 561 | dual_entry.pad.l_stick = pad_state.l_stick; | 455 | } |
| 562 | dual_entry.pad.r_stick = pad_state.r_stick; | 456 | if (controller.is_dual_right_connected) { |
| 563 | 457 | pad_state.connection_status.is_right_connected.Assign(1); | |
| 564 | libnx_entry.connection_status.is_left_connected.Assign(1); | 458 | libnx_state.connection_status.is_right_connected.Assign(1); |
| 565 | libnx_entry.connection_status.is_right_connected.Assign(1); | 459 | } |
| 460 | |||
| 461 | pad_state.sampling_number = | ||
| 462 | npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 463 | npad.joy_dual_lifo.WriteNextEntry(pad_state); | ||
| 566 | break; | 464 | break; |
| 567 | case NPadControllerType::JoyLeft: | 465 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 568 | left_entry.connection_status.raw = 0; | 466 | pad_state.connection_status.raw = 0; |
| 569 | left_entry.connection_status.is_connected.Assign(1); | 467 | pad_state.connection_status.is_connected.Assign(1); |
| 570 | left_entry.connection_status.is_left_connected.Assign(1); | 468 | pad_state.connection_status.is_left_connected.Assign(1); |
| 571 | left_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 469 | |
| 572 | left_entry.pad.l_stick = pad_state.l_stick; | 470 | libnx_state.connection_status.is_left_connected.Assign(1); |
| 573 | left_entry.pad.r_stick = pad_state.r_stick; | 471 | pad_state.sampling_number = |
| 574 | 472 | npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | |
| 575 | libnx_entry.connection_status.is_left_connected.Assign(1); | 473 | npad.joy_left_lifo.WriteNextEntry(pad_state); |
| 576 | break; | 474 | break; |
| 577 | case NPadControllerType::JoyRight: | 475 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 578 | right_entry.connection_status.raw = 0; | 476 | pad_state.connection_status.raw = 0; |
| 579 | right_entry.connection_status.is_connected.Assign(1); | 477 | pad_state.connection_status.is_connected.Assign(1); |
| 580 | right_entry.connection_status.is_right_connected.Assign(1); | 478 | pad_state.connection_status.is_right_connected.Assign(1); |
| 581 | right_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 479 | |
| 582 | right_entry.pad.l_stick = pad_state.l_stick; | 480 | libnx_state.connection_status.is_right_connected.Assign(1); |
| 583 | right_entry.pad.r_stick = pad_state.r_stick; | 481 | pad_state.sampling_number = |
| 584 | 482 | npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | |
| 585 | libnx_entry.connection_status.is_right_connected.Assign(1); | 483 | npad.joy_right_lifo.WriteNextEntry(pad_state); |
| 586 | break; | 484 | break; |
| 587 | case NPadControllerType::GameCube: | 485 | case Core::HID::NpadStyleIndex::GameCube: |
| 588 | main_controller.connection_status.raw = 0; | 486 | pad_state.connection_status.raw = 0; |
| 589 | main_controller.connection_status.is_connected.Assign(1); | 487 | pad_state.connection_status.is_connected.Assign(1); |
| 590 | main_controller.connection_status.is_wired.Assign(1); | 488 | pad_state.connection_status.is_wired.Assign(1); |
| 591 | main_controller.pad.pad_states.raw = pad_state.pad_states.raw; | 489 | |
| 592 | main_controller.pad.l_stick = pad_state.l_stick; | 490 | libnx_state.connection_status.is_wired.Assign(1); |
| 593 | main_controller.pad.r_stick = pad_state.r_stick; | 491 | pad_state.sampling_number = |
| 594 | trigger_entry.l_analog = trigger_state.l_analog; | 492 | npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; |
| 595 | trigger_entry.r_analog = trigger_state.r_analog; | 493 | trigger_state.sampling_number = |
| 596 | 494 | npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; | |
| 597 | libnx_entry.connection_status.is_wired.Assign(1); | 495 | npad.fullkey_lifo.WriteNextEntry(pad_state); |
| 496 | npad.gc_trigger_lifo.WriteNextEntry(trigger_state); | ||
| 598 | break; | 497 | break; |
| 599 | case NPadControllerType::Pokeball: | 498 | case Core::HID::NpadStyleIndex::Pokeball: |
| 600 | pokeball_entry.connection_status.raw = 0; | 499 | pad_state.connection_status.raw = 0; |
| 601 | pokeball_entry.connection_status.is_connected.Assign(1); | 500 | pad_state.connection_status.is_connected.Assign(1); |
| 602 | pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 501 | pad_state.sampling_number = |
| 603 | pokeball_entry.pad.l_stick = pad_state.l_stick; | 502 | npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1; |
| 604 | pokeball_entry.pad.r_stick = pad_state.r_stick; | 503 | npad.palma_lifo.WriteNextEntry(pad_state); |
| 504 | break; | ||
| 505 | default: | ||
| 605 | break; | 506 | break; |
| 606 | } | 507 | } |
| 607 | 508 | ||
| 608 | // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate | 509 | libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; |
| 609 | // any controllers. | 510 | libnx_state.l_stick = pad_state.l_stick; |
| 610 | libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 511 | libnx_state.r_stick = pad_state.r_stick; |
| 611 | libnx_entry.pad.l_stick = pad_state.l_stick; | 512 | npad.system_ext_lifo.WriteNextEntry(pad_state); |
| 612 | libnx_entry.pad.r_stick = pad_state.r_stick; | 513 | |
| 514 | press_state |= static_cast<u64>(pad_state.npad_buttons.raw); | ||
| 613 | 515 | ||
| 614 | press_state |= static_cast<u32>(pad_state.pad_states.raw); | 516 | std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), |
| 517 | &controller.shared_memory_entry, sizeof(NpadInternalState)); | ||
| 615 | } | 518 | } |
| 616 | std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), | ||
| 617 | shared_memory_entries.size() * sizeof(NPadEntry)); | ||
| 618 | } | 519 | } |
| 619 | 520 | ||
| 620 | void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 521 | void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| @@ -622,145 +523,138 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing | |||
| 622 | if (!IsControllerActivated()) { | 523 | if (!IsControllerActivated()) { |
| 623 | return; | 524 | return; |
| 624 | } | 525 | } |
| 625 | for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) { | ||
| 626 | auto& npad = shared_memory_entries[i]; | ||
| 627 | |||
| 628 | const auto& controller_type = connected_controllers[i].type; | ||
| 629 | 526 | ||
| 630 | if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { | 527 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 631 | continue; | 528 | auto& controller = controller_data[i]; |
| 632 | } | ||
| 633 | |||
| 634 | const std::array<SixAxisGeneric*, 6> controller_sixaxes{ | ||
| 635 | &npad.sixaxis_fullkey, &npad.sixaxis_handheld, &npad.sixaxis_dual_left, | ||
| 636 | &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right, | ||
| 637 | }; | ||
| 638 | |||
| 639 | for (auto* sixaxis_sensor : controller_sixaxes) { | ||
| 640 | sixaxis_sensor->common.entry_count = 16; | ||
| 641 | sixaxis_sensor->common.total_entry_count = 17; | ||
| 642 | |||
| 643 | const auto& last_entry = | ||
| 644 | sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; | ||
| 645 | |||
| 646 | sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks(); | ||
| 647 | sixaxis_sensor->common.last_entry_index = | ||
| 648 | (sixaxis_sensor->common.last_entry_index + 1) % 17; | ||
| 649 | 529 | ||
| 650 | auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; | 530 | const auto& controller_type = controller.device->GetNpadStyleIndex(); |
| 651 | 531 | ||
| 652 | cur_entry.timestamp = last_entry.timestamp + 1; | 532 | if (controller_type == Core::HID::NpadStyleIndex::None || |
| 653 | cur_entry.timestamp2 = cur_entry.timestamp; | 533 | !controller.device->IsConnected()) { |
| 534 | continue; | ||
| 654 | } | 535 | } |
| 655 | 536 | ||
| 656 | // Try to read sixaxis sensor states | 537 | auto& npad = controller.shared_memory_entry; |
| 657 | std::array<MotionDevice, 2> motion_devices; | 538 | const auto& motion_state = controller.device->GetMotions(); |
| 658 | 539 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | |
| 659 | if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) { | 540 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; |
| 660 | sixaxis_at_rest = true; | 541 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; |
| 661 | for (std::size_t e = 0; e < motion_devices.size(); ++e) { | 542 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; |
| 662 | const auto& device = motions[i][e]; | 543 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; |
| 663 | if (device) { | 544 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; |
| 664 | std::tie(motion_devices[e].accel, motion_devices[e].gyro, | 545 | |
| 665 | motion_devices[e].rotation, motion_devices[e].orientation, | 546 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { |
| 666 | motion_devices[e].quaternion) = device->GetStatus(); | 547 | controller.sixaxis_at_rest = true; |
| 667 | sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f; | 548 | for (std::size_t e = 0; e < motion_state.size(); ++e) { |
| 668 | } | 549 | controller.sixaxis_at_rest = |
| 550 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 669 | } | 551 | } |
| 670 | } | 552 | } |
| 671 | 553 | ||
| 672 | auto& full_sixaxis_entry = | ||
| 673 | npad.sixaxis_fullkey.sixaxis[npad.sixaxis_fullkey.common.last_entry_index]; | ||
| 674 | auto& handheld_sixaxis_entry = | ||
| 675 | npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index]; | ||
| 676 | auto& dual_left_sixaxis_entry = | ||
| 677 | npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index]; | ||
| 678 | auto& dual_right_sixaxis_entry = | ||
| 679 | npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index]; | ||
| 680 | auto& left_sixaxis_entry = | ||
| 681 | npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index]; | ||
| 682 | auto& right_sixaxis_entry = | ||
| 683 | npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index]; | ||
| 684 | |||
| 685 | switch (controller_type) { | 554 | switch (controller_type) { |
| 686 | case NPadControllerType::None: | 555 | case Core::HID::NpadStyleIndex::None: |
| 687 | UNREACHABLE(); | 556 | UNREACHABLE(); |
| 688 | break; | 557 | break; |
| 689 | case NPadControllerType::ProController: | 558 | case Core::HID::NpadStyleIndex::ProController: |
| 690 | full_sixaxis_entry.attribute.raw = 0; | 559 | sixaxis_fullkey_state.attribute.raw = 0; |
| 691 | if (sixaxis_sensors_enabled && motions[i][0]) { | 560 | if (controller.sixaxis_sensor_enabled) { |
| 692 | full_sixaxis_entry.attribute.is_connected.Assign(1); | 561 | sixaxis_fullkey_state.attribute.is_connected.Assign(1); |
| 693 | full_sixaxis_entry.accel = motion_devices[0].accel; | 562 | sixaxis_fullkey_state.accel = motion_state[0].accel; |
| 694 | full_sixaxis_entry.gyro = motion_devices[0].gyro; | 563 | sixaxis_fullkey_state.gyro = motion_state[0].gyro; |
| 695 | full_sixaxis_entry.rotation = motion_devices[0].rotation; | 564 | sixaxis_fullkey_state.rotation = motion_state[0].rotation; |
| 696 | full_sixaxis_entry.orientation = motion_devices[0].orientation; | 565 | sixaxis_fullkey_state.orientation = motion_state[0].orientation; |
| 697 | } | 566 | } |
| 698 | break; | 567 | break; |
| 699 | case NPadControllerType::Handheld: | 568 | case Core::HID::NpadStyleIndex::Handheld: |
| 700 | handheld_sixaxis_entry.attribute.raw = 0; | 569 | sixaxis_handheld_state.attribute.raw = 0; |
| 701 | if (sixaxis_sensors_enabled && motions[i][0]) { | 570 | if (controller.sixaxis_sensor_enabled) { |
| 702 | handheld_sixaxis_entry.attribute.is_connected.Assign(1); | 571 | sixaxis_handheld_state.attribute.is_connected.Assign(1); |
| 703 | handheld_sixaxis_entry.accel = motion_devices[0].accel; | 572 | sixaxis_handheld_state.accel = motion_state[0].accel; |
| 704 | handheld_sixaxis_entry.gyro = motion_devices[0].gyro; | 573 | sixaxis_handheld_state.gyro = motion_state[0].gyro; |
| 705 | handheld_sixaxis_entry.rotation = motion_devices[0].rotation; | 574 | sixaxis_handheld_state.rotation = motion_state[0].rotation; |
| 706 | handheld_sixaxis_entry.orientation = motion_devices[0].orientation; | 575 | sixaxis_handheld_state.orientation = motion_state[0].orientation; |
| 707 | } | 576 | } |
| 708 | break; | 577 | break; |
| 709 | case NPadControllerType::JoyDual: | 578 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 710 | dual_left_sixaxis_entry.attribute.raw = 0; | 579 | sixaxis_dual_left_state.attribute.raw = 0; |
| 711 | dual_right_sixaxis_entry.attribute.raw = 0; | 580 | sixaxis_dual_right_state.attribute.raw = 0; |
| 712 | if (sixaxis_sensors_enabled && motions[i][0]) { | 581 | if (controller.sixaxis_sensor_enabled) { |
| 713 | // Set motion for the left joycon | 582 | // Set motion for the left joycon |
| 714 | dual_left_sixaxis_entry.attribute.is_connected.Assign(1); | 583 | sixaxis_dual_left_state.attribute.is_connected.Assign(1); |
| 715 | dual_left_sixaxis_entry.accel = motion_devices[0].accel; | 584 | sixaxis_dual_left_state.accel = motion_state[0].accel; |
| 716 | dual_left_sixaxis_entry.gyro = motion_devices[0].gyro; | 585 | sixaxis_dual_left_state.gyro = motion_state[0].gyro; |
| 717 | dual_left_sixaxis_entry.rotation = motion_devices[0].rotation; | 586 | sixaxis_dual_left_state.rotation = motion_state[0].rotation; |
| 718 | dual_left_sixaxis_entry.orientation = motion_devices[0].orientation; | 587 | sixaxis_dual_left_state.orientation = motion_state[0].orientation; |
| 719 | } | 588 | } |
| 720 | if (sixaxis_sensors_enabled && motions[i][1]) { | 589 | if (controller.sixaxis_sensor_enabled) { |
| 721 | // Set motion for the right joycon | 590 | // Set motion for the right joycon |
| 722 | dual_right_sixaxis_entry.attribute.is_connected.Assign(1); | 591 | sixaxis_dual_right_state.attribute.is_connected.Assign(1); |
| 723 | dual_right_sixaxis_entry.accel = motion_devices[1].accel; | 592 | sixaxis_dual_right_state.accel = motion_state[1].accel; |
| 724 | dual_right_sixaxis_entry.gyro = motion_devices[1].gyro; | 593 | sixaxis_dual_right_state.gyro = motion_state[1].gyro; |
| 725 | dual_right_sixaxis_entry.rotation = motion_devices[1].rotation; | 594 | sixaxis_dual_right_state.rotation = motion_state[1].rotation; |
| 726 | dual_right_sixaxis_entry.orientation = motion_devices[1].orientation; | 595 | sixaxis_dual_right_state.orientation = motion_state[1].orientation; |
| 727 | } | 596 | } |
| 728 | break; | 597 | break; |
| 729 | case NPadControllerType::JoyLeft: | 598 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 730 | left_sixaxis_entry.attribute.raw = 0; | 599 | sixaxis_left_lifo_state.attribute.raw = 0; |
| 731 | if (sixaxis_sensors_enabled && motions[i][0]) { | 600 | if (controller.sixaxis_sensor_enabled) { |
| 732 | left_sixaxis_entry.attribute.is_connected.Assign(1); | 601 | sixaxis_left_lifo_state.attribute.is_connected.Assign(1); |
| 733 | left_sixaxis_entry.accel = motion_devices[0].accel; | 602 | sixaxis_left_lifo_state.accel = motion_state[0].accel; |
| 734 | left_sixaxis_entry.gyro = motion_devices[0].gyro; | 603 | sixaxis_left_lifo_state.gyro = motion_state[0].gyro; |
| 735 | left_sixaxis_entry.rotation = motion_devices[0].rotation; | 604 | sixaxis_left_lifo_state.rotation = motion_state[0].rotation; |
| 736 | left_sixaxis_entry.orientation = motion_devices[0].orientation; | 605 | sixaxis_left_lifo_state.orientation = motion_state[0].orientation; |
| 737 | } | 606 | } |
| 738 | break; | 607 | break; |
| 739 | case NPadControllerType::JoyRight: | 608 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 740 | right_sixaxis_entry.attribute.raw = 0; | 609 | sixaxis_right_lifo_state.attribute.raw = 0; |
| 741 | if (sixaxis_sensors_enabled && motions[i][1]) { | 610 | if (controller.sixaxis_sensor_enabled) { |
| 742 | right_sixaxis_entry.attribute.is_connected.Assign(1); | 611 | sixaxis_right_lifo_state.attribute.is_connected.Assign(1); |
| 743 | right_sixaxis_entry.accel = motion_devices[1].accel; | 612 | sixaxis_right_lifo_state.accel = motion_state[1].accel; |
| 744 | right_sixaxis_entry.gyro = motion_devices[1].gyro; | 613 | sixaxis_right_lifo_state.gyro = motion_state[1].gyro; |
| 745 | right_sixaxis_entry.rotation = motion_devices[1].rotation; | 614 | sixaxis_right_lifo_state.rotation = motion_state[1].rotation; |
| 746 | right_sixaxis_entry.orientation = motion_devices[1].orientation; | 615 | sixaxis_right_lifo_state.orientation = motion_state[1].orientation; |
| 747 | } | 616 | } |
| 748 | break; | 617 | break; |
| 749 | case NPadControllerType::GameCube: | 618 | default: |
| 750 | case NPadControllerType::Pokeball: | ||
| 751 | break; | 619 | break; |
| 752 | } | 620 | } |
| 621 | |||
| 622 | sixaxis_fullkey_state.sampling_number = | ||
| 623 | npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 624 | sixaxis_handheld_state.sampling_number = | ||
| 625 | npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 626 | sixaxis_dual_left_state.sampling_number = | ||
| 627 | npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 628 | sixaxis_dual_right_state.sampling_number = | ||
| 629 | npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 630 | sixaxis_left_lifo_state.sampling_number = | ||
| 631 | npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 632 | sixaxis_right_lifo_state.sampling_number = | ||
| 633 | npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 634 | |||
| 635 | if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 636 | // This buffer only is updated on handheld on HW | ||
| 637 | npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 638 | } else { | ||
| 639 | // Handheld doesn't update this buffer on HW | ||
| 640 | npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 641 | } | ||
| 642 | |||
| 643 | npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); | ||
| 644 | npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); | ||
| 645 | npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); | ||
| 646 | npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); | ||
| 647 | std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), | ||
| 648 | &controller.shared_memory_entry, sizeof(NpadInternalState)); | ||
| 753 | } | 649 | } |
| 754 | std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), | ||
| 755 | shared_memory_entries.size() * sizeof(NPadEntry)); | ||
| 756 | } | 650 | } |
| 757 | 651 | ||
| 758 | void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) { | 652 | void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { |
| 759 | style.raw = style_set.raw; | 653 | hid_core.SetSupportedStyleTag(style_set); |
| 760 | } | 654 | } |
| 761 | 655 | ||
| 762 | Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const { | 656 | Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { |
| 763 | return style; | 657 | return hid_core.GetSupportedStyleTag(); |
| 764 | } | 658 | } |
| 765 | 659 | ||
| 766 | void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { | 660 | void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { |
| @@ -779,11 +673,11 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { | |||
| 779 | return supported_npad_id_types.size(); | 673 | return supported_npad_id_types.size(); |
| 780 | } | 674 | } |
| 781 | 675 | ||
| 782 | void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { | 676 | void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { |
| 783 | hold_type = joy_hold_type; | 677 | hold_type = joy_hold_type; |
| 784 | } | 678 | } |
| 785 | 679 | ||
| 786 | Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { | 680 | Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { |
| 787 | return hold_type; | 681 | return hold_type; |
| 788 | } | 682 | } |
| 789 | 683 | ||
| @@ -803,29 +697,91 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode | |||
| 803 | return communication_mode; | 697 | return communication_mode; |
| 804 | } | 698 | } |
| 805 | 699 | ||
| 806 | void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) { | 700 | void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, |
| 807 | const std::size_t npad_index = NPadIdToIndex(npad_id); | 701 | NpadJoyAssignmentMode assignment_mode) { |
| 808 | ASSERT(npad_index < shared_memory_entries.size()); | 702 | if (!IsNpadIdValid(npad_id)) { |
| 809 | if (shared_memory_entries[npad_index].assignment_mode != assignment_mode) { | 703 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 810 | shared_memory_entries[npad_index].assignment_mode = assignment_mode; | 704 | return; |
| 705 | } | ||
| 706 | |||
| 707 | auto& controller = GetControllerFromNpadIdType(npad_id); | ||
| 708 | if (controller.shared_memory_entry.assignment_mode != assignment_mode) { | ||
| 709 | controller.shared_memory_entry.assignment_mode = assignment_mode; | ||
| 710 | } | ||
| 711 | |||
| 712 | if (!controller.device->IsConnected()) { | ||
| 713 | return; | ||
| 714 | } | ||
| 715 | |||
| 716 | if (assignment_mode == NpadJoyAssignmentMode::Dual) { | ||
| 717 | if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) { | ||
| 718 | DisconnectNpad(npad_id); | ||
| 719 | controller.is_dual_left_connected = true; | ||
| 720 | controller.is_dual_right_connected = false; | ||
| 721 | UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); | ||
| 722 | return; | ||
| 723 | } | ||
| 724 | if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { | ||
| 725 | DisconnectNpad(npad_id); | ||
| 726 | controller.is_dual_left_connected = false; | ||
| 727 | controller.is_dual_right_connected = true; | ||
| 728 | UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); | ||
| 729 | return; | ||
| 730 | } | ||
| 731 | return; | ||
| 732 | } | ||
| 733 | |||
| 734 | // This is for NpadJoyAssignmentMode::Single | ||
| 735 | |||
| 736 | // Only JoyconDual get affected by this function | ||
| 737 | if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 738 | return; | ||
| 739 | } | ||
| 740 | |||
| 741 | if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { | ||
| 742 | DisconnectNpad(npad_id); | ||
| 743 | UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); | ||
| 744 | return; | ||
| 745 | } | ||
| 746 | if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { | ||
| 747 | DisconnectNpad(npad_id); | ||
| 748 | UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); | ||
| 749 | return; | ||
| 750 | } | ||
| 751 | |||
| 752 | // We have two controllers connected to the same npad_id we need to split them | ||
| 753 | const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId(); | ||
| 754 | auto& controller_2 = GetControllerFromNpadIdType(npad_id_2); | ||
| 755 | DisconnectNpad(npad_id); | ||
| 756 | if (npad_device_type == NpadJoyDeviceType::Left) { | ||
| 757 | UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); | ||
| 758 | controller_2.is_dual_left_connected = false; | ||
| 759 | controller_2.is_dual_right_connected = true; | ||
| 760 | UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true); | ||
| 761 | } else { | ||
| 762 | UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); | ||
| 763 | controller_2.is_dual_left_connected = true; | ||
| 764 | controller_2.is_dual_right_connected = false; | ||
| 765 | UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true); | ||
| 811 | } | 766 | } |
| 812 | } | 767 | } |
| 813 | 768 | ||
| 814 | bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, | 769 | bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, |
| 815 | const VibrationValue& vibration_value) { | 770 | std::size_t device_index, |
| 816 | if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) { | 771 | const Core::HID::VibrationValue& vibration_value) { |
| 772 | auto& controller = GetControllerFromNpadIdType(npad_id); | ||
| 773 | if (!controller.device->IsConnected()) { | ||
| 817 | return false; | 774 | return false; |
| 818 | } | 775 | } |
| 819 | 776 | ||
| 820 | const auto& player = Settings::values.players.GetValue()[npad_index]; | 777 | if (!controller.device->IsVibrationEnabled()) { |
| 821 | 778 | if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || | |
| 822 | if (!player.vibration_enabled) { | 779 | controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { |
| 823 | if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f || | ||
| 824 | latest_vibration_values[npad_index][device_index].amp_high != 0.0f) { | ||
| 825 | // Send an empty vibration to stop any vibrations. | 780 | // Send an empty vibration to stop any vibrations. |
| 826 | vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f); | 781 | Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f}; |
| 782 | controller.device->SetVibration(device_index, vibration); | ||
| 827 | // Then reset the vibration value to its default value. | 783 | // Then reset the vibration value to its default value. |
| 828 | latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE; | 784 | controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE; |
| 829 | } | 785 | } |
| 830 | 786 | ||
| 831 | return false; | 787 | return false; |
| @@ -839,27 +795,25 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size | |||
| 839 | const auto now = steady_clock::now(); | 795 | const auto now = steady_clock::now(); |
| 840 | 796 | ||
| 841 | // Filter out non-zero vibrations that are within 10ms of each other. | 797 | // Filter out non-zero vibrations that are within 10ms of each other. |
| 842 | if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) && | 798 | if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && |
| 843 | duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) < | 799 | duration_cast<milliseconds>( |
| 800 | now - controller.vibration[device_index].last_vibration_timepoint) < | ||
| 844 | milliseconds(10)) { | 801 | milliseconds(10)) { |
| 845 | return false; | 802 | return false; |
| 846 | } | 803 | } |
| 847 | 804 | ||
| 848 | last_vibration_timepoints[npad_index][device_index] = now; | 805 | controller.vibration[device_index].last_vibration_timepoint = now; |
| 849 | } | 806 | } |
| 850 | 807 | ||
| 851 | auto& vibration = vibrations[npad_index][device_index]; | 808 | Core::HID::VibrationValue vibration{ |
| 852 | const auto player_vibration_strength = static_cast<f32>(player.vibration_strength); | 809 | vibration_value.low_amplitude, vibration_value.low_frequency, |
| 853 | const auto amp_low = | 810 | vibration_value.high_amplitude, vibration_value.high_frequency}; |
| 854 | std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f); | 811 | return controller.device->SetVibration(device_index, vibration); |
| 855 | const auto amp_high = | ||
| 856 | std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f); | ||
| 857 | return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high, | ||
| 858 | vibration_value.freq_high); | ||
| 859 | } | 812 | } |
| 860 | 813 | ||
| 861 | void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, | 814 | void Controller_NPad::VibrateController( |
| 862 | const VibrationValue& vibration_value) { | 815 | const Core::HID::VibrationDeviceHandle& vibration_device_handle, |
| 816 | const Core::HID::VibrationValue& vibration_value) { | ||
| 863 | if (!IsDeviceHandleValid(vibration_device_handle)) { | 817 | if (!IsDeviceHandleValid(vibration_device_handle)) { |
| 864 | return; | 818 | return; |
| 865 | } | 819 | } |
| @@ -868,42 +822,45 @@ void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_han | |||
| 868 | return; | 822 | return; |
| 869 | } | 823 | } |
| 870 | 824 | ||
| 871 | const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); | 825 | auto& controller = GetControllerFromHandle(vibration_device_handle); |
| 872 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); | 826 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); |
| 873 | 827 | ||
| 874 | if (!vibration_devices_mounted[npad_index][device_index] || | 828 | if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) { |
| 875 | !connected_controllers[npad_index].is_connected) { | ||
| 876 | return; | 829 | return; |
| 877 | } | 830 | } |
| 878 | 831 | ||
| 879 | if (vibration_device_handle.device_index == DeviceIndex::None) { | 832 | if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) { |
| 880 | UNREACHABLE_MSG("DeviceIndex should never be None!"); | 833 | UNREACHABLE_MSG("DeviceIndex should never be None!"); |
| 881 | return; | 834 | return; |
| 882 | } | 835 | } |
| 883 | 836 | ||
| 884 | // Some games try to send mismatched parameters in the device handle, block these. | 837 | // Some games try to send mismatched parameters in the device handle, block these. |
| 885 | if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && | 838 | if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft && |
| 886 | (vibration_device_handle.npad_type == NpadType::JoyconRight || | 839 | (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight || |
| 887 | vibration_device_handle.device_index == DeviceIndex::Right)) || | 840 | vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) || |
| 888 | (connected_controllers[npad_index].type == NPadControllerType::JoyRight && | 841 | (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight && |
| 889 | (vibration_device_handle.npad_type == NpadType::JoyconLeft || | 842 | (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft || |
| 890 | vibration_device_handle.device_index == DeviceIndex::Left))) { | 843 | vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) { |
| 891 | return; | 844 | return; |
| 892 | } | 845 | } |
| 893 | 846 | ||
| 894 | // Filter out vibrations with equivalent values to reduce unnecessary state changes. | 847 | // Filter out vibrations with equivalent values to reduce unnecessary state changes. |
| 895 | if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low && | 848 | if (vibration_value.low_amplitude == |
| 896 | vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) { | 849 | controller.vibration[device_index].latest_vibration_value.low_amplitude && |
| 850 | vibration_value.high_amplitude == | ||
| 851 | controller.vibration[device_index].latest_vibration_value.high_amplitude) { | ||
| 897 | return; | 852 | return; |
| 898 | } | 853 | } |
| 899 | 854 | ||
| 900 | if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) { | 855 | if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index, |
| 901 | latest_vibration_values[npad_index][device_index] = vibration_value; | 856 | vibration_value)) { |
| 857 | controller.vibration[device_index].latest_vibration_value = vibration_value; | ||
| 902 | } | 858 | } |
| 903 | } | 859 | } |
| 904 | 860 | ||
| 905 | void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, | 861 | void Controller_NPad::VibrateControllers( |
| 906 | const std::vector<VibrationValue>& vibration_values) { | 862 | const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles, |
| 863 | const std::vector<Core::HID::VibrationValue>& vibration_values) { | ||
| 907 | if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { | 864 | if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { |
| 908 | return; | 865 | return; |
| 909 | } | 866 | } |
| @@ -918,168 +875,285 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat | |||
| 918 | } | 875 | } |
| 919 | } | 876 | } |
| 920 | 877 | ||
| 921 | Controller_NPad::VibrationValue Controller_NPad::GetLastVibration( | 878 | Core::HID::VibrationValue Controller_NPad::GetLastVibration( |
| 922 | const DeviceHandle& vibration_device_handle) const { | 879 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { |
| 923 | if (!IsDeviceHandleValid(vibration_device_handle)) { | 880 | if (!IsDeviceHandleValid(vibration_device_handle)) { |
| 924 | return {}; | 881 | return {}; |
| 925 | } | 882 | } |
| 926 | 883 | ||
| 927 | const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); | 884 | const auto& controller = GetControllerFromHandle(vibration_device_handle); |
| 928 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); | 885 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); |
| 929 | return latest_vibration_values[npad_index][device_index]; | 886 | return controller.vibration[device_index].latest_vibration_value; |
| 930 | } | 887 | } |
| 931 | 888 | ||
| 932 | void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) { | 889 | void Controller_NPad::InitializeVibrationDevice( |
| 890 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) { | ||
| 933 | if (!IsDeviceHandleValid(vibration_device_handle)) { | 891 | if (!IsDeviceHandleValid(vibration_device_handle)) { |
| 934 | return; | 892 | return; |
| 935 | } | 893 | } |
| 936 | 894 | ||
| 937 | const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); | 895 | const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id); |
| 938 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); | 896 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); |
| 939 | InitializeVibrationDeviceAtIndex(npad_index, device_index); | 897 | InitializeVibrationDeviceAtIndex(npad_index, device_index); |
| 940 | } | 898 | } |
| 941 | 899 | ||
| 942 | void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, | 900 | void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, |
| 943 | std::size_t device_index) { | 901 | std::size_t device_index) { |
| 902 | auto& controller = GetControllerFromNpadIdType(npad_id); | ||
| 944 | if (!Settings::values.vibration_enabled.GetValue()) { | 903 | if (!Settings::values.vibration_enabled.GetValue()) { |
| 945 | vibration_devices_mounted[npad_index][device_index] = false; | 904 | controller.vibration[device_index].device_mounted = false; |
| 946 | return; | 905 | return; |
| 947 | } | 906 | } |
| 948 | 907 | ||
| 949 | if (vibrations[npad_index][device_index]) { | 908 | controller.vibration[device_index].device_mounted = |
| 950 | vibration_devices_mounted[npad_index][device_index] = | 909 | controller.device->TestVibration(device_index); |
| 951 | vibrations[npad_index][device_index]->GetStatus() == 1; | ||
| 952 | } else { | ||
| 953 | vibration_devices_mounted[npad_index][device_index] = false; | ||
| 954 | } | ||
| 955 | } | 910 | } |
| 956 | 911 | ||
| 957 | void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { | 912 | void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { |
| 958 | permit_vibration_session_enabled = permit_vibration_session; | 913 | permit_vibration_session_enabled = permit_vibration_session; |
| 959 | } | 914 | } |
| 960 | 915 | ||
| 961 | bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const { | 916 | bool Controller_NPad::IsVibrationDeviceMounted( |
| 917 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | ||
| 962 | if (!IsDeviceHandleValid(vibration_device_handle)) { | 918 | if (!IsDeviceHandleValid(vibration_device_handle)) { |
| 963 | return false; | 919 | return false; |
| 964 | } | 920 | } |
| 965 | 921 | ||
| 966 | const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); | 922 | const auto& controller = GetControllerFromHandle(vibration_device_handle); |
| 967 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); | 923 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); |
| 968 | return vibration_devices_mounted[npad_index][device_index]; | 924 | return controller.vibration[device_index].device_mounted; |
| 969 | } | 925 | } |
| 970 | 926 | ||
| 971 | Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) { | 927 | Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { |
| 972 | return styleset_changed_events[NPadIdToIndex(npad_id)]->GetReadableEvent(); | 928 | if (!IsNpadIdValid(npad_id)) { |
| 929 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 930 | // Fallback to player 1 | ||
| 931 | const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1); | ||
| 932 | return controller.styleset_changed_event->GetReadableEvent(); | ||
| 933 | } | ||
| 934 | |||
| 935 | const auto& controller = GetControllerFromNpadIdType(npad_id); | ||
| 936 | return controller.styleset_changed_event->GetReadableEvent(); | ||
| 973 | } | 937 | } |
| 974 | 938 | ||
| 975 | void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const { | 939 | void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { |
| 976 | styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent().Signal(); | 940 | const auto& controller = GetControllerFromNpadIdType(npad_id); |
| 941 | controller.styleset_changed_event->GetWritableEvent().Signal(); | ||
| 977 | } | 942 | } |
| 978 | 943 | ||
| 979 | void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { | 944 | void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, |
| 980 | UpdateControllerAt(controller, npad_index, true); | 945 | Core::HID::NpadIdType npad_id) { |
| 946 | UpdateControllerAt(controller, npad_id, true); | ||
| 981 | } | 947 | } |
| 982 | 948 | ||
| 983 | void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, | 949 | void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, |
| 984 | bool connected) { | 950 | Core::HID::NpadIdType npad_id, bool connected) { |
| 951 | auto& controller = GetControllerFromNpadIdType(npad_id); | ||
| 985 | if (!connected) { | 952 | if (!connected) { |
| 986 | DisconnectNpadAtIndex(npad_index); | 953 | DisconnectNpad(npad_id); |
| 987 | return; | ||
| 988 | } | ||
| 989 | |||
| 990 | if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) { | ||
| 991 | Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type = | ||
| 992 | MapNPadToSettingsType(controller); | ||
| 993 | Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true; | ||
| 994 | connected_controllers[HANDHELD_INDEX] = {controller, true}; | ||
| 995 | InitNewlyAddedController(HANDHELD_INDEX); | ||
| 996 | return; | 954 | return; |
| 997 | } | 955 | } |
| 998 | 956 | ||
| 999 | Settings::values.players.GetValue()[npad_index].controller_type = | 957 | controller.device->SetNpadStyleIndex(type); |
| 1000 | MapNPadToSettingsType(controller); | 958 | InitNewlyAddedController(npad_id); |
| 1001 | Settings::values.players.GetValue()[npad_index].connected = true; | ||
| 1002 | connected_controllers[npad_index] = {controller, true}; | ||
| 1003 | InitNewlyAddedController(npad_index); | ||
| 1004 | } | 959 | } |
| 1005 | 960 | ||
| 1006 | void Controller_NPad::DisconnectNpad(u32 npad_id) { | 961 | void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { |
| 1007 | DisconnectNpadAtIndex(NPadIdToIndex(npad_id)); | 962 | if (!IsNpadIdValid(npad_id)) { |
| 1008 | } | 963 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 964 | return; | ||
| 965 | } | ||
| 1009 | 966 | ||
| 1010 | void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) { | 967 | LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); |
| 1011 | for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) { | 968 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 969 | for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) { | ||
| 1012 | // Send an empty vibration to stop any vibrations. | 970 | // Send an empty vibration to stop any vibrations. |
| 1013 | VibrateControllerAtIndex(npad_index, device_idx, {}); | 971 | VibrateControllerAtIndex(npad_id, device_idx, {}); |
| 1014 | vibration_devices_mounted[npad_index][device_idx] = false; | 972 | controller.vibration[device_idx].device_mounted = false; |
| 1015 | } | 973 | } |
| 1016 | 974 | ||
| 1017 | Settings::values.players.GetValue()[npad_index].connected = false; | 975 | auto& shared_memory_entry = controller.shared_memory_entry; |
| 1018 | connected_controllers[npad_index].is_connected = false; | 976 | // Don't reset shared_memory_entry.assignment_mode this value is persistent |
| 1019 | 977 | shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out | |
| 1020 | auto& controller = shared_memory_entries[npad_index]; | 978 | shared_memory_entry.device_type.raw = 0; |
| 1021 | controller.style_set.raw = 0; // Zero out | 979 | shared_memory_entry.system_properties.raw = 0; |
| 1022 | controller.device_type.raw = 0; | 980 | shared_memory_entry.button_properties.raw = 0; |
| 1023 | controller.system_properties.raw = 0; | 981 | shared_memory_entry.battery_level_dual = 0; |
| 1024 | controller.button_properties.raw = 0; | 982 | shared_memory_entry.battery_level_left = 0; |
| 1025 | controller.battery_level_dual = 0; | 983 | shared_memory_entry.battery_level_right = 0; |
| 1026 | controller.battery_level_left = 0; | 984 | shared_memory_entry.fullkey_color = { |
| 1027 | controller.battery_level_right = 0; | 985 | .attribute = ColorAttribute::NoController, |
| 1028 | controller.fullkey_color = {}; | 986 | .fullkey = {}, |
| 1029 | controller.joycon_color = {}; | 987 | }; |
| 1030 | controller.assignment_mode = NpadAssignments::Dual; | 988 | shared_memory_entry.joycon_color = { |
| 1031 | controller.footer_type = AppletFooterUiType::None; | 989 | .attribute = ColorAttribute::NoController, |
| 990 | .left = {}, | ||
| 991 | .right = {}, | ||
| 992 | }; | ||
| 993 | shared_memory_entry.applet_footer.type = AppletFooterUiType::None; | ||
| 994 | |||
| 995 | controller.is_dual_left_connected = true; | ||
| 996 | controller.is_dual_right_connected = true; | ||
| 997 | controller.is_connected = false; | ||
| 998 | controller.device->Disconnect(); | ||
| 999 | SignalStyleSetChangedEvent(npad_id); | ||
| 1000 | WriteEmptyEntry(controller.shared_memory_entry); | ||
| 1001 | } | ||
| 1032 | 1002 | ||
| 1033 | SignalStyleSetChangedEvent(IndexToNPad(npad_index)); | 1003 | void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, |
| 1004 | GyroscopeZeroDriftMode drift_mode) { | ||
| 1005 | if (!IsDeviceHandleValid(sixaxis_handle)) { | ||
| 1006 | LOG_ERROR(Service_HID, "Invalid handle"); | ||
| 1007 | return; | ||
| 1008 | } | ||
| 1009 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1010 | controller.gyroscope_zero_drift_mode = drift_mode; | ||
| 1034 | } | 1011 | } |
| 1035 | 1012 | ||
| 1036 | void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { | 1013 | Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode( |
| 1037 | gyroscope_zero_drift_mode = drift_mode; | 1014 | Core::HID::SixAxisSensorHandle sixaxis_handle) const { |
| 1015 | if (!IsDeviceHandleValid(sixaxis_handle)) { | ||
| 1016 | LOG_ERROR(Service_HID, "Invalid handle"); | ||
| 1017 | // Return the default value | ||
| 1018 | return GyroscopeZeroDriftMode::Standard; | ||
| 1019 | } | ||
| 1020 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1021 | return controller.gyroscope_zero_drift_mode; | ||
| 1038 | } | 1022 | } |
| 1039 | 1023 | ||
| 1040 | Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const { | 1024 | bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const { |
| 1041 | return gyroscope_zero_drift_mode; | 1025 | if (!IsDeviceHandleValid(sixaxis_handle)) { |
| 1026 | LOG_ERROR(Service_HID, "Invalid handle"); | ||
| 1027 | // Return the default value | ||
| 1028 | return true; | ||
| 1029 | } | ||
| 1030 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1031 | return controller.sixaxis_at_rest; | ||
| 1042 | } | 1032 | } |
| 1043 | 1033 | ||
| 1044 | bool Controller_NPad::IsSixAxisSensorAtRest() const { | 1034 | void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, |
| 1045 | return sixaxis_at_rest; | 1035 | bool sixaxis_status) { |
| 1036 | if (!IsDeviceHandleValid(sixaxis_handle)) { | ||
| 1037 | LOG_ERROR(Service_HID, "Invalid handle"); | ||
| 1038 | return; | ||
| 1039 | } | ||
| 1040 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1041 | controller.sixaxis_sensor_enabled = sixaxis_status; | ||
| 1046 | } | 1042 | } |
| 1047 | 1043 | ||
| 1048 | void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) { | 1044 | void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, |
| 1049 | sixaxis_sensors_enabled = six_axis_status; | 1045 | bool sixaxis_fusion_status) { |
| 1046 | if (!IsDeviceHandleValid(sixaxis_handle)) { | ||
| 1047 | LOG_ERROR(Service_HID, "Invalid handle"); | ||
| 1048 | return; | ||
| 1049 | } | ||
| 1050 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1051 | controller.sixaxis_fusion_enabled = sixaxis_fusion_status; | ||
| 1050 | } | 1052 | } |
| 1051 | 1053 | ||
| 1052 | void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) { | 1054 | void Controller_NPad::SetSixAxisFusionParameters( |
| 1053 | sixaxis_fusion_parameter1 = parameter1; | 1055 | Core::HID::SixAxisSensorHandle sixaxis_handle, |
| 1054 | sixaxis_fusion_parameter2 = parameter2; | 1056 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { |
| 1057 | if (!IsDeviceHandleValid(sixaxis_handle)) { | ||
| 1058 | LOG_ERROR(Service_HID, "Invalid handle"); | ||
| 1059 | return; | ||
| 1060 | } | ||
| 1061 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1062 | controller.sixaxis_fusion = sixaxis_fusion_parameters; | ||
| 1055 | } | 1063 | } |
| 1056 | 1064 | ||
| 1057 | std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() { | 1065 | Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters( |
| 1058 | return { | 1066 | Core::HID::SixAxisSensorHandle sixaxis_handle) { |
| 1059 | sixaxis_fusion_parameter1, | 1067 | if (!IsDeviceHandleValid(sixaxis_handle)) { |
| 1060 | sixaxis_fusion_parameter2, | 1068 | LOG_ERROR(Service_HID, "Invalid handle"); |
| 1061 | }; | 1069 | // Since these parameters are unknow just return zeros |
| 1070 | return {}; | ||
| 1071 | } | ||
| 1072 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1073 | return controller.sixaxis_fusion; | ||
| 1062 | } | 1074 | } |
| 1063 | 1075 | ||
| 1064 | void Controller_NPad::ResetSixAxisFusionParameters() { | 1076 | void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) { |
| 1065 | sixaxis_fusion_parameter1 = 0.0f; | 1077 | if (!IsDeviceHandleValid(sixaxis_handle)) { |
| 1066 | sixaxis_fusion_parameter2 = 0.0f; | 1078 | LOG_ERROR(Service_HID, "Invalid handle"); |
| 1079 | return; | ||
| 1080 | } | ||
| 1081 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1082 | // Since these parameters are unknow just fill with zeros | ||
| 1083 | controller.sixaxis_fusion = {}; | ||
| 1067 | } | 1084 | } |
| 1068 | 1085 | ||
| 1069 | void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { | 1086 | void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, |
| 1070 | const auto npad_index_1 = NPadIdToIndex(npad_id_1); | 1087 | Core::HID::NpadIdType npad_id_2) { |
| 1071 | const auto npad_index_2 = NPadIdToIndex(npad_id_2); | 1088 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { |
| 1089 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, | ||
| 1090 | npad_id_2); | ||
| 1091 | return; | ||
| 1092 | } | ||
| 1093 | auto& controller_1 = GetControllerFromNpadIdType(npad_id_1); | ||
| 1094 | auto& controller_2 = GetControllerFromNpadIdType(npad_id_2); | ||
| 1095 | const auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); | ||
| 1096 | const auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); | ||
| 1097 | bool merge_controllers = false; | ||
| 1072 | 1098 | ||
| 1073 | // If the controllers at both npad indices form a pair of left and right joycons, merge them. | 1099 | // If the controllers at both npad indices form a pair of left and right joycons, merge them. |
| 1074 | // Otherwise, do nothing. | 1100 | // Otherwise, do nothing. |
| 1075 | if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft && | 1101 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && |
| 1076 | connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) || | 1102 | controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) { |
| 1077 | (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && | 1103 | merge_controllers = true; |
| 1078 | connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { | 1104 | } |
| 1105 | if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft && | ||
| 1106 | controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) { | ||
| 1107 | merge_controllers = true; | ||
| 1108 | } | ||
| 1109 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && | ||
| 1110 | controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight && | ||
| 1111 | controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) { | ||
| 1112 | merge_controllers = true; | ||
| 1113 | } | ||
| 1114 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && | ||
| 1115 | controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft && | ||
| 1116 | !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) { | ||
| 1117 | merge_controllers = true; | ||
| 1118 | } | ||
| 1119 | if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && | ||
| 1120 | controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight && | ||
| 1121 | controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { | ||
| 1122 | merge_controllers = true; | ||
| 1123 | } | ||
| 1124 | if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && | ||
| 1125 | controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && | ||
| 1126 | !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { | ||
| 1127 | merge_controllers = true; | ||
| 1128 | } | ||
| 1129 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && | ||
| 1130 | controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && | ||
| 1131 | controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected && | ||
| 1132 | !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { | ||
| 1133 | merge_controllers = true; | ||
| 1134 | } | ||
| 1135 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && | ||
| 1136 | controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual && | ||
| 1137 | !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected && | ||
| 1138 | controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { | ||
| 1139 | merge_controllers = true; | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | if (merge_controllers) { | ||
| 1079 | // Disconnect the joycon at the second id and connect the dual joycon at the first index. | 1143 | // Disconnect the joycon at the second id and connect the dual joycon at the first index. |
| 1080 | DisconnectNpad(npad_id_2); | 1144 | DisconnectNpad(npad_id_2); |
| 1081 | AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); | 1145 | controller_1.is_dual_left_connected = true; |
| 1146 | controller_1.is_dual_right_connected = true; | ||
| 1147 | AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); | ||
| 1148 | return; | ||
| 1082 | } | 1149 | } |
| 1150 | LOG_WARNING(Service_HID, | ||
| 1151 | "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, " | ||
| 1152 | "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}", | ||
| 1153 | npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(), | ||
| 1154 | controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected, | ||
| 1155 | controller_1.is_dual_right_connected, controller_2.is_dual_left_connected, | ||
| 1156 | controller_2.is_dual_right_connected); | ||
| 1083 | } | 1157 | } |
| 1084 | 1158 | ||
| 1085 | void Controller_NPad::StartLRAssignmentMode() { | 1159 | void Controller_NPad::StartLRAssignmentMode() { |
| @@ -1092,61 +1166,66 @@ void Controller_NPad::StopLRAssignmentMode() { | |||
| 1092 | is_in_lr_assignment_mode = false; | 1166 | is_in_lr_assignment_mode = false; |
| 1093 | } | 1167 | } |
| 1094 | 1168 | ||
| 1095 | bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { | 1169 | bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, |
| 1096 | if (npad_id_1 == NPAD_HANDHELD || npad_id_2 == NPAD_HANDHELD || npad_id_1 == NPAD_UNKNOWN || | 1170 | Core::HID::NpadIdType npad_id_2) { |
| 1097 | npad_id_2 == NPAD_UNKNOWN) { | 1171 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { |
| 1172 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, | ||
| 1173 | npad_id_2); | ||
| 1174 | return false; | ||
| 1175 | } | ||
| 1176 | if (npad_id_1 == Core::HID::NpadIdType::Handheld || | ||
| 1177 | npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other || | ||
| 1178 | npad_id_2 == Core::HID::NpadIdType::Other) { | ||
| 1098 | return true; | 1179 | return true; |
| 1099 | } | 1180 | } |
| 1100 | const auto npad_index_1 = NPadIdToIndex(npad_id_1); | 1181 | const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device; |
| 1101 | const auto npad_index_2 = NPadIdToIndex(npad_id_2); | 1182 | const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device; |
| 1102 | 1183 | const auto type_index_1 = controller_1->GetNpadStyleIndex(); | |
| 1103 | if (!IsControllerSupported(connected_controllers[npad_index_1].type) || | 1184 | const auto type_index_2 = controller_2->GetNpadStyleIndex(); |
| 1104 | !IsControllerSupported(connected_controllers[npad_index_2].type)) { | 1185 | const auto is_connected_1 = controller_1->IsConnected(); |
| 1186 | const auto is_connected_2 = controller_2->IsConnected(); | ||
| 1187 | |||
| 1188 | if (!IsControllerSupported(type_index_1) && is_connected_1) { | ||
| 1189 | return false; | ||
| 1190 | } | ||
| 1191 | if (!IsControllerSupported(type_index_2) && is_connected_2) { | ||
| 1105 | return false; | 1192 | return false; |
| 1106 | } | 1193 | } |
| 1107 | 1194 | ||
| 1108 | std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); | 1195 | UpdateControllerAt(type_index_2, npad_id_1, is_connected_2); |
| 1109 | 1196 | UpdateControllerAt(type_index_1, npad_id_2, is_connected_1); | |
| 1110 | AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1); | ||
| 1111 | AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2); | ||
| 1112 | 1197 | ||
| 1113 | return true; | 1198 | return true; |
| 1114 | } | 1199 | } |
| 1115 | 1200 | ||
| 1116 | Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { | 1201 | Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) { |
| 1117 | if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { | 1202 | if (!IsNpadIdValid(npad_id)) { |
| 1118 | // These are controllers without led patterns | 1203 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1119 | return LedPattern{0, 0, 0, 0}; | 1204 | return Core::HID::LedPattern{0, 0, 0, 0}; |
| 1120 | } | ||
| 1121 | switch (npad_id) { | ||
| 1122 | case 0: | ||
| 1123 | return LedPattern{1, 0, 0, 0}; | ||
| 1124 | case 1: | ||
| 1125 | return LedPattern{1, 1, 0, 0}; | ||
| 1126 | case 2: | ||
| 1127 | return LedPattern{1, 1, 1, 0}; | ||
| 1128 | case 3: | ||
| 1129 | return LedPattern{1, 1, 1, 1}; | ||
| 1130 | case 4: | ||
| 1131 | return LedPattern{1, 0, 0, 1}; | ||
| 1132 | case 5: | ||
| 1133 | return LedPattern{1, 0, 1, 0}; | ||
| 1134 | case 6: | ||
| 1135 | return LedPattern{1, 0, 1, 1}; | ||
| 1136 | case 7: | ||
| 1137 | return LedPattern{0, 1, 1, 0}; | ||
| 1138 | default: | ||
| 1139 | return LedPattern{0, 0, 0, 0}; | ||
| 1140 | } | 1205 | } |
| 1206 | const auto& controller = GetControllerFromNpadIdType(npad_id).device; | ||
| 1207 | return controller->GetLedPattern(); | ||
| 1141 | } | 1208 | } |
| 1142 | 1209 | ||
| 1143 | bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const { | 1210 | bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled( |
| 1144 | return unintended_home_button_input_protection[NPadIdToIndex(npad_id)]; | 1211 | Core::HID::NpadIdType npad_id) const { |
| 1212 | if (!IsNpadIdValid(npad_id)) { | ||
| 1213 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 1214 | // Return the default value | ||
| 1215 | return false; | ||
| 1216 | } | ||
| 1217 | const auto& controller = GetControllerFromNpadIdType(npad_id); | ||
| 1218 | return controller.unintended_home_button_input_protection; | ||
| 1145 | } | 1219 | } |
| 1146 | 1220 | ||
| 1147 | void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, | 1221 | void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, |
| 1148 | u32 npad_id) { | 1222 | Core::HID::NpadIdType npad_id) { |
| 1149 | unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; | 1223 | if (!IsNpadIdValid(npad_id)) { |
| 1224 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 1225 | return; | ||
| 1226 | } | ||
| 1227 | auto& controller = GetControllerFromNpadIdType(npad_id); | ||
| 1228 | controller.unintended_home_button_input_protection = is_protection_enabled; | ||
| 1150 | } | 1229 | } |
| 1151 | 1230 | ||
| 1152 | void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { | 1231 | void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { |
| @@ -1154,49 +1233,51 @@ void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { | |||
| 1154 | } | 1233 | } |
| 1155 | 1234 | ||
| 1156 | void Controller_NPad::ClearAllConnectedControllers() { | 1235 | void Controller_NPad::ClearAllConnectedControllers() { |
| 1157 | for (auto& controller : connected_controllers) { | 1236 | for (auto& controller : controller_data) { |
| 1158 | if (controller.is_connected && controller.type != NPadControllerType::None) { | 1237 | if (controller.device->IsConnected() && |
| 1159 | controller.type = NPadControllerType::None; | 1238 | controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { |
| 1160 | controller.is_connected = false; | 1239 | controller.device->Disconnect(); |
| 1240 | controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); | ||
| 1161 | } | 1241 | } |
| 1162 | } | 1242 | } |
| 1163 | } | 1243 | } |
| 1164 | 1244 | ||
| 1165 | void Controller_NPad::DisconnectAllConnectedControllers() { | 1245 | void Controller_NPad::DisconnectAllConnectedControllers() { |
| 1166 | for (auto& controller : connected_controllers) { | 1246 | for (auto& controller : controller_data) { |
| 1167 | controller.is_connected = false; | 1247 | controller.device->Disconnect(); |
| 1168 | } | 1248 | } |
| 1169 | } | 1249 | } |
| 1170 | 1250 | ||
| 1171 | void Controller_NPad::ConnectAllDisconnectedControllers() { | 1251 | void Controller_NPad::ConnectAllDisconnectedControllers() { |
| 1172 | for (auto& controller : connected_controllers) { | 1252 | for (auto& controller : controller_data) { |
| 1173 | if (controller.type != NPadControllerType::None && !controller.is_connected) { | 1253 | if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && |
| 1174 | controller.is_connected = true; | 1254 | !controller.device->IsConnected()) { |
| 1255 | controller.device->Connect(); | ||
| 1175 | } | 1256 | } |
| 1176 | } | 1257 | } |
| 1177 | } | 1258 | } |
| 1178 | 1259 | ||
| 1179 | void Controller_NPad::ClearAllControllers() { | 1260 | void Controller_NPad::ClearAllControllers() { |
| 1180 | for (auto& controller : connected_controllers) { | 1261 | for (auto& controller : controller_data) { |
| 1181 | controller.type = NPadControllerType::None; | 1262 | controller.device->Disconnect(); |
| 1182 | controller.is_connected = false; | 1263 | controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); |
| 1183 | } | 1264 | } |
| 1184 | } | 1265 | } |
| 1185 | 1266 | ||
| 1186 | u32 Controller_NPad::GetAndResetPressState() { | 1267 | Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { |
| 1187 | return press_state.exchange(0); | 1268 | return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); |
| 1188 | } | 1269 | } |
| 1189 | 1270 | ||
| 1190 | bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { | 1271 | bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { |
| 1191 | if (controller == NPadControllerType::Handheld) { | 1272 | if (controller == Core::HID::NpadStyleIndex::Handheld) { |
| 1192 | const bool support_handheld = | 1273 | const bool support_handheld = |
| 1193 | std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), | 1274 | std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), |
| 1194 | NPAD_HANDHELD) != supported_npad_id_types.end(); | 1275 | Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end(); |
| 1195 | // Handheld is not even a supported type, lets stop here | 1276 | // Handheld is not even a supported type, lets stop here |
| 1196 | if (!support_handheld) { | 1277 | if (!support_handheld) { |
| 1197 | return false; | 1278 | return false; |
| 1198 | } | 1279 | } |
| 1199 | // Handheld should not be supported in docked mode | 1280 | // Handheld shouldn't be supported in docked mode |
| 1200 | if (Settings::values.use_docked_mode.GetValue()) { | 1281 | if (Settings::values.use_docked_mode.GetValue()) { |
| 1201 | return false; | 1282 | return false; |
| 1202 | } | 1283 | } |
| @@ -1205,20 +1286,31 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const | |||
| 1205 | } | 1286 | } |
| 1206 | 1287 | ||
| 1207 | if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), | 1288 | if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), |
| 1208 | [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) { | 1289 | [](Core::HID::NpadIdType npad_id) { |
| 1290 | return npad_id <= Core::HID::NpadIdType::Player8; | ||
| 1291 | })) { | ||
| 1292 | Core::HID::NpadStyleTag style = GetSupportedStyleSet(); | ||
| 1209 | switch (controller) { | 1293 | switch (controller) { |
| 1210 | case NPadControllerType::ProController: | 1294 | case Core::HID::NpadStyleIndex::ProController: |
| 1211 | return style.fullkey; | 1295 | return style.fullkey; |
| 1212 | case NPadControllerType::JoyDual: | 1296 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 1213 | return style.joycon_dual; | 1297 | return style.joycon_dual; |
| 1214 | case NPadControllerType::JoyLeft: | 1298 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1215 | return style.joycon_left; | 1299 | return style.joycon_left; |
| 1216 | case NPadControllerType::JoyRight: | 1300 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1217 | return style.joycon_right; | 1301 | return style.joycon_right; |
| 1218 | case NPadControllerType::GameCube: | 1302 | case Core::HID::NpadStyleIndex::GameCube: |
| 1219 | return style.gamecube; | 1303 | return style.gamecube; |
| 1220 | case NPadControllerType::Pokeball: | 1304 | case Core::HID::NpadStyleIndex::Pokeball: |
| 1221 | return style.palma; | 1305 | return style.palma; |
| 1306 | case Core::HID::NpadStyleIndex::NES: | ||
| 1307 | return style.lark; | ||
| 1308 | case Core::HID::NpadStyleIndex::SNES: | ||
| 1309 | return style.lucia; | ||
| 1310 | case Core::HID::NpadStyleIndex::N64: | ||
| 1311 | return style.lagoon; | ||
| 1312 | case Core::HID::NpadStyleIndex::SegaGenesis: | ||
| 1313 | return style.lager; | ||
| 1222 | default: | 1314 | default: |
| 1223 | return false; | 1315 | return false; |
| 1224 | } | 1316 | } |
| @@ -1227,4 +1319,48 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const | |||
| 1227 | return false; | 1319 | return false; |
| 1228 | } | 1320 | } |
| 1229 | 1321 | ||
| 1322 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | ||
| 1323 | const Core::HID::SixAxisSensorHandle& device_handle) { | ||
| 1324 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 1325 | return GetControllerFromNpadIdType(npad_id); | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | ||
| 1329 | const Core::HID::SixAxisSensorHandle& device_handle) const { | ||
| 1330 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 1331 | return GetControllerFromNpadIdType(npad_id); | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | ||
| 1335 | const Core::HID::VibrationDeviceHandle& device_handle) { | ||
| 1336 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 1337 | return GetControllerFromNpadIdType(npad_id); | ||
| 1338 | } | ||
| 1339 | |||
| 1340 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | ||
| 1341 | const Core::HID::VibrationDeviceHandle& device_handle) const { | ||
| 1342 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 1343 | return GetControllerFromNpadIdType(npad_id); | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( | ||
| 1347 | Core::HID::NpadIdType npad_id) { | ||
| 1348 | if (!IsNpadIdValid(npad_id)) { | ||
| 1349 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 1350 | npad_id = Core::HID::NpadIdType::Player1; | ||
| 1351 | } | ||
| 1352 | const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); | ||
| 1353 | return controller_data[npad_index]; | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( | ||
| 1357 | Core::HID::NpadIdType npad_id) const { | ||
| 1358 | if (!IsNpadIdValid(npad_id)) { | ||
| 1359 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 1360 | npad_id = Core::HID::NpadIdType::Player1; | ||
| 1361 | } | ||
| 1362 | const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); | ||
| 1363 | return controller_data[npad_index]; | ||
| 1364 | } | ||
| 1365 | |||
| 1230 | } // namespace Service::HID | 1366 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 9ee146caf..63281cb35 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -11,9 +11,14 @@ | |||
| 11 | #include "common/bit_field.h" | 11 | #include "common/bit_field.h" |
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/quaternion.h" | 13 | #include "common/quaternion.h" |
| 14 | #include "common/settings.h" | 14 | #include "core/hid/hid_types.h" |
| 15 | #include "core/frontend/input.h" | ||
| 16 | #include "core/hle/service/hid/controllers/controller_base.h" | 15 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 16 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 17 | |||
| 18 | namespace Core::HID { | ||
| 19 | class EmulatedController; | ||
| 20 | enum class ControllerTriggerType; | ||
| 21 | } // namespace Core::HID | ||
| 17 | 22 | ||
| 18 | namespace Kernel { | 23 | namespace Kernel { |
| 19 | class KEvent; | 24 | class KEvent; |
| @@ -26,12 +31,9 @@ class ServiceContext; | |||
| 26 | 31 | ||
| 27 | namespace Service::HID { | 32 | namespace Service::HID { |
| 28 | 33 | ||
| 29 | constexpr u32 NPAD_HANDHELD = 32; | ||
| 30 | constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? | ||
| 31 | |||
| 32 | class Controller_NPad final : public ControllerBase { | 34 | class Controller_NPad final : public ControllerBase { |
| 33 | public: | 35 | public: |
| 34 | explicit Controller_NPad(Core::System& system_, | 36 | explicit Controller_NPad(Core::HID::HIDCore& hid_core_, |
| 35 | KernelHelpers::ServiceContext& service_context_); | 37 | KernelHelpers::ServiceContext& service_context_); |
| 36 | ~Controller_NPad() override; | 38 | ~Controller_NPad() override; |
| 37 | 39 | ||
| @@ -48,60 +50,39 @@ public: | |||
| 48 | void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 50 | void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 49 | std::size_t size) override; | 51 | std::size_t size) override; |
| 50 | 52 | ||
| 51 | // Called when input devices should be loaded | 53 | // This is nn::hid::GyroscopeZeroDriftMode |
| 52 | void OnLoadInputDevices() override; | ||
| 53 | |||
| 54 | enum class NPadControllerType { | ||
| 55 | None, | ||
| 56 | ProController, | ||
| 57 | Handheld, | ||
| 58 | JoyDual, | ||
| 59 | JoyLeft, | ||
| 60 | JoyRight, | ||
| 61 | GameCube, | ||
| 62 | Pokeball, | ||
| 63 | }; | ||
| 64 | |||
| 65 | enum class NpadType : u8 { | ||
| 66 | ProController = 3, | ||
| 67 | Handheld = 4, | ||
| 68 | JoyconDual = 5, | ||
| 69 | JoyconLeft = 6, | ||
| 70 | JoyconRight = 7, | ||
| 71 | GameCube = 8, | ||
| 72 | Pokeball = 9, | ||
| 73 | MaxNpadType = 10, | ||
| 74 | }; | ||
| 75 | |||
| 76 | enum class DeviceIndex : u8 { | ||
| 77 | Left = 0, | ||
| 78 | Right = 1, | ||
| 79 | None = 2, | ||
| 80 | MaxDeviceIndex = 3, | ||
| 81 | }; | ||
| 82 | |||
| 83 | enum class GyroscopeZeroDriftMode : u32 { | 54 | enum class GyroscopeZeroDriftMode : u32 { |
| 84 | Loose = 0, | 55 | Loose = 0, |
| 85 | Standard = 1, | 56 | Standard = 1, |
| 86 | Tight = 2, | 57 | Tight = 2, |
| 87 | }; | 58 | }; |
| 88 | 59 | ||
| 89 | enum class NpadHoldType : u64 { | 60 | // This is nn::hid::NpadJoyHoldType |
| 61 | enum class NpadJoyHoldType : u64 { | ||
| 90 | Vertical = 0, | 62 | Vertical = 0, |
| 91 | Horizontal = 1, | 63 | Horizontal = 1, |
| 92 | }; | 64 | }; |
| 93 | 65 | ||
| 94 | enum class NpadAssignments : u32 { | 66 | // This is nn::hid::NpadJoyAssignmentMode |
| 67 | enum class NpadJoyAssignmentMode : u32 { | ||
| 95 | Dual = 0, | 68 | Dual = 0, |
| 96 | Single = 1, | 69 | Single = 1, |
| 97 | }; | 70 | }; |
| 98 | 71 | ||
| 72 | // This is nn::hid::NpadJoyDeviceType | ||
| 73 | enum class NpadJoyDeviceType : s64 { | ||
| 74 | Left = 0, | ||
| 75 | Right = 1, | ||
| 76 | }; | ||
| 77 | |||
| 78 | // This is nn::hid::NpadHandheldActivationMode | ||
| 99 | enum class NpadHandheldActivationMode : u64 { | 79 | enum class NpadHandheldActivationMode : u64 { |
| 100 | Dual = 0, | 80 | Dual = 0, |
| 101 | Single = 1, | 81 | Single = 1, |
| 102 | None = 2, | 82 | None = 2, |
| 103 | }; | 83 | }; |
| 104 | 84 | ||
| 85 | // This is nn::hid::NpadCommunicationMode | ||
| 105 | enum class NpadCommunicationMode : u64 { | 86 | enum class NpadCommunicationMode : u64 { |
| 106 | Mode_5ms = 0, | 87 | Mode_5ms = 0, |
| 107 | Mode_10ms = 1, | 88 | Mode_10ms = 1, |
| @@ -109,74 +90,22 @@ public: | |||
| 109 | Default = 3, | 90 | Default = 3, |
| 110 | }; | 91 | }; |
| 111 | 92 | ||
| 112 | struct DeviceHandle { | 93 | static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{ |
| 113 | NpadType npad_type; | 94 | .low_amplitude = 0.0f, |
| 114 | u8 npad_id; | 95 | .low_frequency = 160.0f, |
| 115 | DeviceIndex device_index; | 96 | .high_amplitude = 0.0f, |
| 116 | INSERT_PADDING_BYTES_NOINIT(1); | 97 | .high_frequency = 320.0f, |
| 117 | }; | 98 | }; |
| 118 | static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size"); | ||
| 119 | 99 | ||
| 120 | struct NpadStyleSet { | 100 | void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); |
| 121 | union { | 101 | Core::HID::NpadStyleTag GetSupportedStyleSet() const; |
| 122 | u32_le raw{}; | ||
| 123 | |||
| 124 | BitField<0, 1, u32> fullkey; | ||
| 125 | BitField<1, 1, u32> handheld; | ||
| 126 | BitField<2, 1, u32> joycon_dual; | ||
| 127 | BitField<3, 1, u32> joycon_left; | ||
| 128 | BitField<4, 1, u32> joycon_right; | ||
| 129 | BitField<5, 1, u32> gamecube; | ||
| 130 | BitField<6, 1, u32> palma; | ||
| 131 | BitField<7, 1, u32> lark; | ||
| 132 | BitField<8, 1, u32> handheld_lark; | ||
| 133 | BitField<9, 1, u32> lucia; | ||
| 134 | BitField<29, 1, u32> system_ext; | ||
| 135 | BitField<30, 1, u32> system; | ||
| 136 | }; | ||
| 137 | }; | ||
| 138 | static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); | ||
| 139 | |||
| 140 | struct VibrationValue { | ||
| 141 | f32 amp_low; | ||
| 142 | f32 freq_low; | ||
| 143 | f32 amp_high; | ||
| 144 | f32 freq_high; | ||
| 145 | }; | ||
| 146 | static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size"); | ||
| 147 | |||
| 148 | static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{ | ||
| 149 | .amp_low = 0.0f, | ||
| 150 | .freq_low = 160.0f, | ||
| 151 | .amp_high = 0.0f, | ||
| 152 | .freq_high = 320.0f, | ||
| 153 | }; | ||
| 154 | |||
| 155 | struct LedPattern { | ||
| 156 | explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { | ||
| 157 | position1.Assign(light1); | ||
| 158 | position2.Assign(light2); | ||
| 159 | position3.Assign(light3); | ||
| 160 | position4.Assign(light4); | ||
| 161 | } | ||
| 162 | union { | ||
| 163 | u64 raw{}; | ||
| 164 | BitField<0, 1, u64> position1; | ||
| 165 | BitField<1, 1, u64> position2; | ||
| 166 | BitField<2, 1, u64> position3; | ||
| 167 | BitField<3, 1, u64> position4; | ||
| 168 | }; | ||
| 169 | }; | ||
| 170 | |||
| 171 | void SetSupportedStyleSet(NpadStyleSet style_set); | ||
| 172 | NpadStyleSet GetSupportedStyleSet() const; | ||
| 173 | 102 | ||
| 174 | void SetSupportedNpadIdTypes(u8* data, std::size_t length); | 103 | void SetSupportedNpadIdTypes(u8* data, std::size_t length); |
| 175 | void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); | 104 | void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); |
| 176 | std::size_t GetSupportedNpadIdTypesSize() const; | 105 | std::size_t GetSupportedNpadIdTypesSize() const; |
| 177 | 106 | ||
| 178 | void SetHoldType(NpadHoldType joy_hold_type); | 107 | void SetHoldType(NpadJoyHoldType joy_hold_type); |
| 179 | NpadHoldType GetHoldType() const; | 108 | NpadJoyHoldType GetHoldType() const; |
| 180 | 109 | ||
| 181 | void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); | 110 | void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); |
| 182 | NpadHandheldActivationMode GetNpadHandheldActivationMode() const; | 111 | NpadHandheldActivationMode GetNpadHandheldActivationMode() const; |
| @@ -184,162 +113,107 @@ public: | |||
| 184 | void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); | 113 | void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); |
| 185 | NpadCommunicationMode GetNpadCommunicationMode() const; | 114 | NpadCommunicationMode GetNpadCommunicationMode() const; |
| 186 | 115 | ||
| 187 | void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); | 116 | void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, |
| 117 | NpadJoyAssignmentMode assignment_mode); | ||
| 188 | 118 | ||
| 189 | bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, | 119 | bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, |
| 190 | const VibrationValue& vibration_value); | 120 | const Core::HID::VibrationValue& vibration_value); |
| 191 | 121 | ||
| 192 | void VibrateController(const DeviceHandle& vibration_device_handle, | 122 | void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, |
| 193 | const VibrationValue& vibration_value); | 123 | const Core::HID::VibrationValue& vibration_value); |
| 194 | 124 | ||
| 195 | void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, | 125 | void VibrateControllers( |
| 196 | const std::vector<VibrationValue>& vibration_values); | 126 | const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles, |
| 127 | const std::vector<Core::HID::VibrationValue>& vibration_values); | ||
| 197 | 128 | ||
| 198 | VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const; | 129 | Core::HID::VibrationValue GetLastVibration( |
| 130 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; | ||
| 199 | 131 | ||
| 200 | void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle); | 132 | void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle); |
| 201 | 133 | ||
| 202 | void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index); | 134 | void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index); |
| 203 | 135 | ||
| 204 | void SetPermitVibrationSession(bool permit_vibration_session); | 136 | void SetPermitVibrationSession(bool permit_vibration_session); |
| 205 | 137 | ||
| 206 | bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const; | 138 | bool IsVibrationDeviceMounted( |
| 139 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; | ||
| 207 | 140 | ||
| 208 | Kernel::KReadableEvent& GetStyleSetChangedEvent(u32 npad_id); | 141 | Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id); |
| 209 | void SignalStyleSetChangedEvent(u32 npad_id) const; | 142 | void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const; |
| 210 | 143 | ||
| 211 | // Adds a new controller at an index. | 144 | // Adds a new controller at an index. |
| 212 | void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); | 145 | void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id); |
| 213 | // Adds a new controller at an index with connection status. | 146 | // Adds a new controller at an index with connection status. |
| 214 | void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); | 147 | void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, |
| 215 | 148 | bool connected); | |
| 216 | void DisconnectNpad(u32 npad_id); | 149 | |
| 217 | void DisconnectNpadAtIndex(std::size_t index); | 150 | void DisconnectNpad(Core::HID::NpadIdType npad_id); |
| 218 | 151 | ||
| 219 | void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); | 152 | void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, |
| 220 | GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; | 153 | GyroscopeZeroDriftMode drift_mode); |
| 221 | bool IsSixAxisSensorAtRest() const; | 154 | GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode( |
| 222 | void SetSixAxisEnabled(bool six_axis_status); | 155 | Core::HID::SixAxisSensorHandle sixaxis_handle) const; |
| 223 | void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2); | 156 | bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const; |
| 224 | std::pair<f32, f32> GetSixAxisFusionParameters(); | 157 | void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status); |
| 225 | void ResetSixAxisFusionParameters(); | 158 | void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, |
| 226 | LedPattern GetLedPattern(u32 npad_id); | 159 | bool sixaxis_fusion_status); |
| 227 | bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; | 160 | void SetSixAxisFusionParameters( |
| 228 | void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); | 161 | Core::HID::SixAxisSensorHandle sixaxis_handle, |
| 162 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); | ||
| 163 | Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters( | ||
| 164 | Core::HID::SixAxisSensorHandle sixaxis_handle); | ||
| 165 | void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle); | ||
| 166 | Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); | ||
| 167 | bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; | ||
| 168 | void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, | ||
| 169 | Core::HID::NpadIdType npad_id); | ||
| 229 | void SetAnalogStickUseCenterClamp(bool use_center_clamp); | 170 | void SetAnalogStickUseCenterClamp(bool use_center_clamp); |
| 230 | void ClearAllConnectedControllers(); | 171 | void ClearAllConnectedControllers(); |
| 231 | void DisconnectAllConnectedControllers(); | 172 | void DisconnectAllConnectedControllers(); |
| 232 | void ConnectAllDisconnectedControllers(); | 173 | void ConnectAllDisconnectedControllers(); |
| 233 | void ClearAllControllers(); | 174 | void ClearAllControllers(); |
| 234 | 175 | ||
| 235 | void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2); | 176 | void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); |
| 236 | void StartLRAssignmentMode(); | 177 | void StartLRAssignmentMode(); |
| 237 | void StopLRAssignmentMode(); | 178 | void StopLRAssignmentMode(); |
| 238 | bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); | 179 | bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); |
| 239 | 180 | ||
| 240 | // Logical OR for all buttons presses on all controllers | 181 | // Logical OR for all buttons presses on all controllers |
| 241 | // Specifically for cheat engine and other features. | 182 | // Specifically for cheat engine and other features. |
| 242 | u32 GetAndResetPressState(); | 183 | Core::HID::NpadButton GetAndResetPressState(); |
| 243 | 184 | ||
| 244 | static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type); | 185 | static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); |
| 245 | static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type); | 186 | static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle); |
| 246 | static std::size_t NPadIdToIndex(u32 npad_id); | 187 | static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); |
| 247 | static u32 IndexToNPad(std::size_t index); | ||
| 248 | static bool IsNpadIdValid(u32 npad_id); | ||
| 249 | static bool IsDeviceHandleValid(const DeviceHandle& device_handle); | ||
| 250 | 188 | ||
| 251 | private: | 189 | private: |
| 252 | struct CommonHeader { | 190 | // This is nn::hid::detail::ColorAttribute |
| 253 | s64_le timestamp; | 191 | enum class ColorAttribute : u32 { |
| 254 | s64_le total_entry_count; | ||
| 255 | s64_le last_entry_index; | ||
| 256 | s64_le entry_count; | ||
| 257 | }; | ||
| 258 | static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); | ||
| 259 | |||
| 260 | enum class ColorAttributes : u32_le { | ||
| 261 | Ok = 0, | 192 | Ok = 0, |
| 262 | ReadError = 1, | 193 | ReadError = 1, |
| 263 | NoController = 2, | 194 | NoController = 2, |
| 264 | }; | 195 | }; |
| 265 | static_assert(sizeof(ColorAttributes) == 4, "ColorAttributes is an invalid size"); | 196 | static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size"); |
| 266 | 197 | ||
| 267 | struct ControllerColor { | 198 | // This is nn::hid::detail::NpadFullKeyColorState |
| 268 | u32_le body; | 199 | struct NpadFullKeyColorState { |
| 269 | u32_le button; | 200 | ColorAttribute attribute; |
| 201 | Core::HID::NpadControllerColor fullkey; | ||
| 270 | }; | 202 | }; |
| 271 | static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size"); | 203 | static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); |
| 272 | 204 | ||
| 273 | struct FullKeyColor { | 205 | // This is nn::hid::detail::NpadJoyColorState |
| 274 | ColorAttributes attribute; | 206 | struct NpadJoyColorState { |
| 275 | ControllerColor fullkey; | 207 | ColorAttribute attribute; |
| 208 | Core::HID::NpadControllerColor left; | ||
| 209 | Core::HID::NpadControllerColor right; | ||
| 276 | }; | 210 | }; |
| 277 | static_assert(sizeof(FullKeyColor) == 0xC, "FullKeyColor is an invalid size"); | 211 | static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); |
| 278 | 212 | ||
| 279 | struct JoyconColor { | 213 | // This is nn::hid::NpadAttribute |
| 280 | ColorAttributes attribute; | 214 | struct NpadAttribute { |
| 281 | ControllerColor left; | ||
| 282 | ControllerColor right; | ||
| 283 | }; | ||
| 284 | static_assert(sizeof(JoyconColor) == 0x14, "JoyconColor is an invalid size"); | ||
| 285 | |||
| 286 | struct ControllerPadState { | ||
| 287 | union { | ||
| 288 | u64_le raw{}; | ||
| 289 | // Button states | ||
| 290 | BitField<0, 1, u64> a; | ||
| 291 | BitField<1, 1, u64> b; | ||
| 292 | BitField<2, 1, u64> x; | ||
| 293 | BitField<3, 1, u64> y; | ||
| 294 | BitField<4, 1, u64> l_stick; | ||
| 295 | BitField<5, 1, u64> r_stick; | ||
| 296 | BitField<6, 1, u64> l; | ||
| 297 | BitField<7, 1, u64> r; | ||
| 298 | BitField<8, 1, u64> zl; | ||
| 299 | BitField<9, 1, u64> zr; | ||
| 300 | BitField<10, 1, u64> plus; | ||
| 301 | BitField<11, 1, u64> minus; | ||
| 302 | |||
| 303 | // D-Pad | ||
| 304 | BitField<12, 1, u64> d_left; | ||
| 305 | BitField<13, 1, u64> d_up; | ||
| 306 | BitField<14, 1, u64> d_right; | ||
| 307 | BitField<15, 1, u64> d_down; | ||
| 308 | |||
| 309 | // Left JoyStick | ||
| 310 | BitField<16, 1, u64> l_stick_left; | ||
| 311 | BitField<17, 1, u64> l_stick_up; | ||
| 312 | BitField<18, 1, u64> l_stick_right; | ||
| 313 | BitField<19, 1, u64> l_stick_down; | ||
| 314 | |||
| 315 | // Right JoyStick | ||
| 316 | BitField<20, 1, u64> r_stick_left; | ||
| 317 | BitField<21, 1, u64> r_stick_up; | ||
| 318 | BitField<22, 1, u64> r_stick_right; | ||
| 319 | BitField<23, 1, u64> r_stick_down; | ||
| 320 | |||
| 321 | // Not always active? | ||
| 322 | BitField<24, 1, u64> left_sl; | ||
| 323 | BitField<25, 1, u64> left_sr; | ||
| 324 | |||
| 325 | BitField<26, 1, u64> right_sl; | ||
| 326 | BitField<27, 1, u64> right_sr; | ||
| 327 | |||
| 328 | BitField<28, 1, u64> palma; | ||
| 329 | BitField<30, 1, u64> handheld_left_b; | ||
| 330 | }; | ||
| 331 | }; | ||
| 332 | static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); | ||
| 333 | |||
| 334 | struct AnalogPosition { | ||
| 335 | s32_le x; | ||
| 336 | s32_le y; | ||
| 337 | }; | ||
| 338 | static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size"); | ||
| 339 | |||
| 340 | struct ConnectionState { | ||
| 341 | union { | 215 | union { |
| 342 | u32_le raw{}; | 216 | u32 raw{}; |
| 343 | BitField<0, 1, u32> is_connected; | 217 | BitField<0, 1, u32> is_connected; |
| 344 | BitField<1, 1, u32> is_wired; | 218 | BitField<1, 1, u32> is_wired; |
| 345 | BitField<2, 1, u32> is_left_connected; | 219 | BitField<2, 1, u32> is_left_connected; |
| @@ -348,79 +222,60 @@ private: | |||
| 348 | BitField<5, 1, u32> is_right_wired; | 222 | BitField<5, 1, u32> is_right_wired; |
| 349 | }; | 223 | }; |
| 350 | }; | 224 | }; |
| 351 | static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); | 225 | static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size"); |
| 352 | 226 | ||
| 353 | struct ControllerPad { | 227 | // This is nn::hid::NpadFullKeyState |
| 354 | ControllerPadState pad_states; | 228 | // This is nn::hid::NpadHandheldState |
| 355 | AnalogPosition l_stick; | 229 | // This is nn::hid::NpadJoyDualState |
| 356 | AnalogPosition r_stick; | 230 | // This is nn::hid::NpadJoyLeftState |
| 357 | }; | 231 | // This is nn::hid::NpadJoyRightState |
| 358 | static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size"); | 232 | // This is nn::hid::NpadPalmaState |
| 359 | 233 | // This is nn::hid::NpadSystemExtState | |
| 360 | struct GenericStates { | 234 | struct NPadGenericState { |
| 361 | s64_le timestamp; | 235 | s64_le sampling_number; |
| 362 | s64_le timestamp2; | 236 | Core::HID::NpadButtonState npad_buttons; |
| 363 | ControllerPad pad; | 237 | Core::HID::AnalogStickState l_stick; |
| 364 | ConnectionState connection_status; | 238 | Core::HID::AnalogStickState r_stick; |
| 365 | }; | 239 | NpadAttribute connection_status; |
| 366 | static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); | 240 | INSERT_PADDING_BYTES(4); // Reserved |
| 367 | |||
| 368 | struct NPadGeneric { | ||
| 369 | CommonHeader common; | ||
| 370 | std::array<GenericStates, 17> npad; | ||
| 371 | }; | 241 | }; |
| 372 | static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); | 242 | static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); |
| 373 | 243 | ||
| 374 | struct SixAxisAttributes { | 244 | // This is nn::hid::SixAxisSensorAttribute |
| 245 | struct SixAxisSensorAttribute { | ||
| 375 | union { | 246 | union { |
| 376 | u32_le raw{}; | 247 | u32 raw{}; |
| 377 | BitField<0, 1, u32> is_connected; | 248 | BitField<0, 1, u32> is_connected; |
| 378 | BitField<1, 1, u32> is_interpolated; | 249 | BitField<1, 1, u32> is_interpolated; |
| 379 | }; | 250 | }; |
| 380 | }; | 251 | }; |
| 381 | static_assert(sizeof(SixAxisAttributes) == 4, "SixAxisAttributes is an invalid size"); | 252 | static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); |
| 382 | 253 | ||
| 383 | struct SixAxisStates { | 254 | // This is nn::hid::SixAxisSensorState |
| 384 | s64_le timestamp{}; | 255 | struct SixAxisSensorState { |
| 385 | INSERT_PADDING_WORDS(2); | 256 | s64 delta_time{}; |
| 386 | s64_le timestamp2{}; | 257 | s64 sampling_number{}; |
| 387 | Common::Vec3f accel{}; | 258 | Common::Vec3f accel{}; |
| 388 | Common::Vec3f gyro{}; | 259 | Common::Vec3f gyro{}; |
| 389 | Common::Vec3f rotation{}; | 260 | Common::Vec3f rotation{}; |
| 390 | std::array<Common::Vec3f, 3> orientation{}; | 261 | std::array<Common::Vec3f, 3> orientation{}; |
| 391 | SixAxisAttributes attribute; | 262 | SixAxisSensorAttribute attribute; |
| 392 | INSERT_PADDING_BYTES(4); // Reserved | 263 | INSERT_PADDING_BYTES(4); // Reserved |
| 393 | }; | 264 | }; |
| 394 | static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size"); | 265 | static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); |
| 395 | |||
| 396 | struct SixAxisGeneric { | ||
| 397 | CommonHeader common{}; | ||
| 398 | std::array<SixAxisStates, 17> sixaxis{}; | ||
| 399 | }; | ||
| 400 | static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size"); | ||
| 401 | 266 | ||
| 402 | struct TriggerState { | 267 | // This is nn::hid::server::NpadGcTriggerState |
| 403 | s64_le timestamp{}; | 268 | struct NpadGcTriggerState { |
| 404 | s64_le timestamp2{}; | 269 | s64 sampling_number{}; |
| 405 | s32_le l_analog{}; | 270 | s32 l_analog{}; |
| 406 | s32_le r_analog{}; | 271 | s32 r_analog{}; |
| 407 | }; | 272 | }; |
| 408 | static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size"); | 273 | static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); |
| 409 | |||
| 410 | struct TriggerGeneric { | ||
| 411 | INSERT_PADDING_BYTES(0x4); | ||
| 412 | s64_le timestamp; | ||
| 413 | INSERT_PADDING_BYTES(0x4); | ||
| 414 | s64_le total_entry_count; | ||
| 415 | s64_le last_entry_index; | ||
| 416 | s64_le entry_count; | ||
| 417 | std::array<TriggerState, 17> trigger{}; | ||
| 418 | }; | ||
| 419 | static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size"); | ||
| 420 | 274 | ||
| 275 | // This is nn::hid::NpadSystemProperties | ||
| 421 | struct NPadSystemProperties { | 276 | struct NPadSystemProperties { |
| 422 | union { | 277 | union { |
| 423 | s64_le raw{}; | 278 | s64 raw{}; |
| 424 | BitField<0, 1, s64> is_charging_joy_dual; | 279 | BitField<0, 1, s64> is_charging_joy_dual; |
| 425 | BitField<1, 1, s64> is_charging_joy_left; | 280 | BitField<1, 1, s64> is_charging_joy_left; |
| 426 | BitField<2, 1, s64> is_charging_joy_right; | 281 | BitField<2, 1, s64> is_charging_joy_right; |
| @@ -438,17 +293,20 @@ private: | |||
| 438 | }; | 293 | }; |
| 439 | static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size"); | 294 | static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size"); |
| 440 | 295 | ||
| 441 | struct NPadButtonProperties { | 296 | // This is nn::hid::NpadSystemButtonProperties |
| 297 | struct NpadSystemButtonProperties { | ||
| 442 | union { | 298 | union { |
| 443 | s32_le raw{}; | 299 | s32 raw{}; |
| 444 | BitField<0, 1, s32> is_home_button_protection_enabled; | 300 | BitField<0, 1, s32> is_home_button_protection_enabled; |
| 445 | }; | 301 | }; |
| 446 | }; | 302 | }; |
| 447 | static_assert(sizeof(NPadButtonProperties) == 0x4, "NPadButtonProperties is an invalid size"); | 303 | static_assert(sizeof(NpadSystemButtonProperties) == 0x4, |
| 304 | "NPadButtonProperties is an invalid size"); | ||
| 448 | 305 | ||
| 449 | struct NPadDevice { | 306 | // This is nn::hid::system::DeviceType |
| 307 | struct DeviceType { | ||
| 450 | union { | 308 | union { |
| 451 | u32_le raw{}; | 309 | u32 raw{}; |
| 452 | BitField<0, 1, s32> fullkey; | 310 | BitField<0, 1, s32> fullkey; |
| 453 | BitField<1, 1, s32> debug_pad; | 311 | BitField<1, 1, s32> debug_pad; |
| 454 | BitField<2, 1, s32> handheld_left; | 312 | BitField<2, 1, s32> handheld_left; |
| @@ -465,26 +323,29 @@ private: | |||
| 465 | BitField<13, 1, s32> handheld_lark_nes_left; | 323 | BitField<13, 1, s32> handheld_lark_nes_left; |
| 466 | BitField<14, 1, s32> handheld_lark_nes_right; | 324 | BitField<14, 1, s32> handheld_lark_nes_right; |
| 467 | BitField<15, 1, s32> lucia; | 325 | BitField<15, 1, s32> lucia; |
| 326 | BitField<16, 1, s32> lagon; | ||
| 327 | BitField<17, 1, s32> lager; | ||
| 468 | BitField<31, 1, s32> system; | 328 | BitField<31, 1, s32> system; |
| 469 | }; | 329 | }; |
| 470 | }; | 330 | }; |
| 471 | 331 | ||
| 472 | struct MotionDevice { | 332 | // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl |
| 473 | Common::Vec3f accel; | 333 | struct NfcXcdDeviceHandleStateImpl { |
| 474 | Common::Vec3f gyro; | 334 | u64 handle; |
| 475 | Common::Vec3f rotation; | 335 | bool is_available; |
| 476 | std::array<Common::Vec3f, 3> orientation; | 336 | bool is_activated; |
| 477 | Common::Quaternion<f32> quaternion; | 337 | INSERT_PADDING_BYTES(0x6); // Reserved |
| 478 | }; | 338 | u64 sampling_number; |
| 479 | |||
| 480 | struct NfcXcdHandle { | ||
| 481 | INSERT_PADDING_BYTES(0x60); | ||
| 482 | }; | 339 | }; |
| 340 | static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, | ||
| 341 | "NfcXcdDeviceHandleStateImpl is an invalid size"); | ||
| 483 | 342 | ||
| 343 | // This is nn::hid::system::AppletFooterUiAttributesSet | ||
| 484 | struct AppletFooterUiAttributes { | 344 | struct AppletFooterUiAttributes { |
| 485 | INSERT_PADDING_BYTES(0x4); | 345 | INSERT_PADDING_BYTES(0x4); |
| 486 | }; | 346 | }; |
| 487 | 347 | ||
| 348 | // This is nn::hid::system::AppletFooterUiType | ||
| 488 | enum class AppletFooterUiType : u8 { | 349 | enum class AppletFooterUiType : u8 { |
| 489 | None = 0, | 350 | None = 0, |
| 490 | HandheldNone = 1, | 351 | HandheldNone = 1, |
| @@ -510,95 +371,153 @@ private: | |||
| 510 | Lagon = 21, | 371 | Lagon = 21, |
| 511 | }; | 372 | }; |
| 512 | 373 | ||
| 513 | struct NPadEntry { | 374 | struct AppletFooterUi { |
| 514 | NpadStyleSet style_set; | 375 | AppletFooterUiAttributes attributes; |
| 515 | NpadAssignments assignment_mode; | 376 | AppletFooterUiType type; |
| 516 | FullKeyColor fullkey_color; | 377 | INSERT_PADDING_BYTES(0x5B); // Reserved |
| 517 | JoyconColor joycon_color; | 378 | }; |
| 518 | 379 | static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); | |
| 519 | NPadGeneric fullkey_states; | 380 | |
| 520 | NPadGeneric handheld_states; | 381 | // This is nn::hid::NpadLarkType |
| 521 | NPadGeneric joy_dual_states; | 382 | enum class NpadLarkType : u32 { |
| 522 | NPadGeneric joy_left_states; | 383 | Invalid, |
| 523 | NPadGeneric joy_right_states; | 384 | H1, |
| 524 | NPadGeneric palma_states; | 385 | H2, |
| 525 | NPadGeneric system_ext_states; | 386 | NL, |
| 526 | SixAxisGeneric sixaxis_fullkey; | 387 | NR, |
| 527 | SixAxisGeneric sixaxis_handheld; | 388 | }; |
| 528 | SixAxisGeneric sixaxis_dual_left; | 389 | |
| 529 | SixAxisGeneric sixaxis_dual_right; | 390 | // This is nn::hid::NpadLuciaType |
| 530 | SixAxisGeneric sixaxis_left; | 391 | enum class NpadLuciaType : u32 { |
| 531 | SixAxisGeneric sixaxis_right; | 392 | Invalid, |
| 532 | NPadDevice device_type; | 393 | J, |
| 533 | INSERT_PADDING_BYTES(0x4); // reserved | 394 | E, |
| 395 | U, | ||
| 396 | }; | ||
| 397 | |||
| 398 | // This is nn::hid::NpadLagonType | ||
| 399 | enum class NpadLagonType : u32 { | ||
| 400 | Invalid, | ||
| 401 | }; | ||
| 402 | |||
| 403 | // This is nn::hid::NpadLagerType | ||
| 404 | enum class NpadLagerType : u32 { | ||
| 405 | Invalid, | ||
| 406 | J, | ||
| 407 | E, | ||
| 408 | U, | ||
| 409 | }; | ||
| 410 | |||
| 411 | // This is nn::hid::detail::NpadInternalState | ||
| 412 | struct NpadInternalState { | ||
| 413 | Core::HID::NpadStyleTag style_tag; | ||
| 414 | NpadJoyAssignmentMode assignment_mode; | ||
| 415 | NpadFullKeyColorState fullkey_color; | ||
| 416 | NpadJoyColorState joycon_color; | ||
| 417 | Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; | ||
| 418 | Lifo<NPadGenericState, hid_entry_count> handheld_lifo; | ||
| 419 | Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; | ||
| 420 | Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; | ||
| 421 | Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; | ||
| 422 | Lifo<NPadGenericState, hid_entry_count> palma_lifo; | ||
| 423 | Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; | ||
| 424 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; | ||
| 425 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; | ||
| 426 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; | ||
| 427 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; | ||
| 428 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; | ||
| 429 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; | ||
| 430 | DeviceType device_type; | ||
| 431 | INSERT_PADDING_BYTES(0x4); // Reserved | ||
| 534 | NPadSystemProperties system_properties; | 432 | NPadSystemProperties system_properties; |
| 535 | NPadButtonProperties button_properties; | 433 | NpadSystemButtonProperties button_properties; |
| 536 | u32 battery_level_dual; | 434 | Core::HID::NpadBatteryLevel battery_level_dual; |
| 537 | u32 battery_level_left; | 435 | Core::HID::NpadBatteryLevel battery_level_left; |
| 538 | u32 battery_level_right; | 436 | Core::HID::NpadBatteryLevel battery_level_right; |
| 539 | AppletFooterUiAttributes footer_attributes; | 437 | union { |
| 540 | AppletFooterUiType footer_type; | 438 | Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; |
| 541 | // nfc_states needs to be checked switchbrew does not match with HW | 439 | AppletFooterUi applet_footer; |
| 542 | NfcXcdHandle nfc_states; | 440 | }; |
| 543 | INSERT_PADDING_BYTES(0x8); // Mutex | 441 | INSERT_PADDING_BYTES(0x20); // Unknown |
| 544 | TriggerGeneric gc_trigger_states; | 442 | Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; |
| 545 | INSERT_PADDING_BYTES(0xc1f); | 443 | NpadLarkType lark_type_l_and_main; |
| 546 | }; | 444 | NpadLarkType lark_type_r; |
| 547 | static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); | 445 | NpadLuciaType lucia_type; |
| 548 | 446 | NpadLagonType lagon_type; | |
| 549 | struct ControllerHolder { | 447 | NpadLagerType lager_type; |
| 550 | NPadControllerType type; | 448 | // FW 13.x Investigate there is some sort of bitflag related to joycons |
| 551 | bool is_connected; | 449 | INSERT_PADDING_BYTES(0x4); |
| 552 | }; | 450 | INSERT_PADDING_BYTES(0xc08); // Unknown |
| 553 | 451 | }; | |
| 554 | void InitNewlyAddedController(std::size_t controller_idx); | 452 | static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size"); |
| 555 | bool IsControllerSupported(NPadControllerType controller) const; | 453 | |
| 556 | void RequestPadStateUpdate(u32 npad_id); | 454 | struct VibrationData { |
| 557 | 455 | bool device_mounted{}; | |
| 558 | std::atomic<u32> press_state{}; | 456 | Core::HID::VibrationValue latest_vibration_value{}; |
| 559 | 457 | std::chrono::steady_clock::time_point last_vibration_timepoint{}; | |
| 560 | NpadStyleSet style{}; | 458 | }; |
| 561 | std::array<NPadEntry, 10> shared_memory_entries{}; | 459 | |
| 562 | using ButtonArray = std::array< | 460 | struct NpadControllerData { |
| 563 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, | 461 | Core::HID::EmulatedController* device; |
| 564 | 10>; | 462 | Kernel::KEvent* styleset_changed_event{}; |
| 565 | using StickArray = std::array< | 463 | NpadInternalState shared_memory_entry{}; |
| 566 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, | 464 | |
| 567 | 10>; | 465 | std::array<VibrationData, 2> vibration{}; |
| 568 | using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>, | 466 | bool unintended_home_button_input_protection{}; |
| 569 | Settings::NativeVibration::NUM_VIBRATIONS_HID>, | 467 | bool is_connected{}; |
| 570 | 10>; | 468 | |
| 571 | using MotionArray = std::array< | 469 | // Dual joycons can have only one side connected |
| 572 | std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>, | 470 | bool is_dual_left_connected{true}; |
| 573 | 10>; | 471 | bool is_dual_right_connected{true}; |
| 574 | 472 | ||
| 473 | // Motion parameters | ||
| 474 | bool sixaxis_at_rest{true}; | ||
| 475 | bool sixaxis_sensor_enabled{true}; | ||
| 476 | bool sixaxis_fusion_enabled{false}; | ||
| 477 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{}; | ||
| 478 | GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; | ||
| 479 | |||
| 480 | // Current pad state | ||
| 481 | NPadGenericState npad_pad_state{}; | ||
| 482 | NPadGenericState npad_libnx_state{}; | ||
| 483 | NpadGcTriggerState npad_trigger_state{}; | ||
| 484 | SixAxisSensorState sixaxis_fullkey_state{}; | ||
| 485 | SixAxisSensorState sixaxis_handheld_state{}; | ||
| 486 | SixAxisSensorState sixaxis_dual_left_state{}; | ||
| 487 | SixAxisSensorState sixaxis_dual_right_state{}; | ||
| 488 | SixAxisSensorState sixaxis_left_lifo_state{}; | ||
| 489 | SixAxisSensorState sixaxis_right_lifo_state{}; | ||
| 490 | int callback_key; | ||
| 491 | }; | ||
| 492 | |||
| 493 | void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx); | ||
| 494 | void InitNewlyAddedController(Core::HID::NpadIdType npad_id); | ||
| 495 | bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const; | ||
| 496 | void RequestPadStateUpdate(Core::HID::NpadIdType npad_id); | ||
| 497 | void WriteEmptyEntry(NpadInternalState& npad); | ||
| 498 | |||
| 499 | NpadControllerData& GetControllerFromHandle( | ||
| 500 | const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 501 | const NpadControllerData& GetControllerFromHandle( | ||
| 502 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 503 | NpadControllerData& GetControllerFromHandle( | ||
| 504 | const Core::HID::VibrationDeviceHandle& device_handle); | ||
| 505 | const NpadControllerData& GetControllerFromHandle( | ||
| 506 | const Core::HID::VibrationDeviceHandle& device_handle) const; | ||
| 507 | NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); | ||
| 508 | const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; | ||
| 509 | |||
| 510 | std::atomic<u64> press_state{}; | ||
| 511 | |||
| 512 | std::array<NpadControllerData, 10> controller_data{}; | ||
| 575 | KernelHelpers::ServiceContext& service_context; | 513 | KernelHelpers::ServiceContext& service_context; |
| 576 | std::mutex mutex; | 514 | std::mutex mutex; |
| 577 | ButtonArray buttons; | 515 | std::vector<Core::HID::NpadIdType> supported_npad_id_types{}; |
| 578 | StickArray sticks; | 516 | NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical}; |
| 579 | VibrationArray vibrations; | ||
| 580 | MotionArray motions; | ||
| 581 | std::vector<u32> supported_npad_id_types{}; | ||
| 582 | NpadHoldType hold_type{NpadHoldType::Vertical}; | ||
| 583 | NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; | 517 | NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; |
| 584 | NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; | 518 | NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; |
| 585 | // Each controller should have their own styleset changed event | ||
| 586 | std::array<Kernel::KEvent*, 10> styleset_changed_events{}; | ||
| 587 | std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> | ||
| 588 | last_vibration_timepoints{}; | ||
| 589 | std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{}; | ||
| 590 | bool permit_vibration_session_enabled{false}; | 519 | bool permit_vibration_session_enabled{false}; |
| 591 | std::array<std::array<bool, 2>, 10> vibration_devices_mounted{}; | ||
| 592 | std::array<ControllerHolder, 10> connected_controllers{}; | ||
| 593 | std::array<bool, 10> unintended_home_button_input_protection{}; | ||
| 594 | bool analog_stick_use_center_clamp{}; | 520 | bool analog_stick_use_center_clamp{}; |
| 595 | GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; | ||
| 596 | bool sixaxis_sensors_enabled{true}; | ||
| 597 | f32 sixaxis_fusion_parameter1{}; | ||
| 598 | f32 sixaxis_fusion_parameter2{}; | ||
| 599 | bool sixaxis_at_rest{true}; | ||
| 600 | std::array<ControllerPad, 10> npad_pad_states{}; | ||
| 601 | std::array<TriggerState, 10> npad_trigger_states{}; | ||
| 602 | bool is_in_lr_assignment_mode{false}; | 521 | bool is_in_lr_assignment_mode{false}; |
| 603 | }; | 522 | }; |
| 604 | } // namespace Service::HID | 523 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 772c20453..b7d7a5756 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp | |||
| @@ -5,11 +5,12 @@ | |||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/hid/hid_core.h" | ||
| 8 | #include "core/hle/service/hid/controllers/stubbed.h" | 9 | #include "core/hle/service/hid/controllers/stubbed.h" |
| 9 | 10 | ||
| 10 | namespace Service::HID { | 11 | namespace Service::HID { |
| 11 | 12 | ||
| 12 | Controller_Stubbed::Controller_Stubbed(Core::System& system_) : ControllerBase{system_} {} | 13 | Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} |
| 13 | Controller_Stubbed::~Controller_Stubbed() = default; | 14 | Controller_Stubbed::~Controller_Stubbed() = default; |
| 14 | 15 | ||
| 15 | void Controller_Stubbed::OnInit() {} | 16 | void Controller_Stubbed::OnInit() {} |
| @@ -31,10 +32,9 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u | |||
| 31 | std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); | 32 | std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | void Controller_Stubbed::OnLoadInputDevices() {} | ||
| 35 | |||
| 36 | void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { | 35 | void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { |
| 37 | common_offset = off; | 36 | common_offset = off; |
| 38 | smart_update = true; | 37 | smart_update = true; |
| 39 | } | 38 | } |
| 39 | |||
| 40 | } // namespace Service::HID | 40 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 21092af0d..0044a4efa 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | class Controller_Stubbed final : public ControllerBase { | 11 | class Controller_Stubbed final : public ControllerBase { |
| 12 | public: | 12 | public: |
| 13 | explicit Controller_Stubbed(Core::System& system_); | 13 | explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_); |
| 14 | ~Controller_Stubbed() override; | 14 | ~Controller_Stubbed() override; |
| 15 | 15 | ||
| 16 | // Called when the controller is initialized | 16 | // Called when the controller is initialized |
| @@ -22,12 +22,17 @@ public: | |||
| 22 | // When the controller is requesting an update for the shared memory | 22 | // When the controller is requesting an update for the shared memory |
| 23 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; | 23 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; |
| 24 | 24 | ||
| 25 | // Called when input devices should be loaded | ||
| 26 | void OnLoadInputDevices() override; | ||
| 27 | |||
| 28 | void SetCommonHeaderOffset(std::size_t off); | 25 | void SetCommonHeaderOffset(std::size_t off); |
| 29 | 26 | ||
| 30 | private: | 27 | private: |
| 28 | struct CommonHeader { | ||
| 29 | s64 timestamp; | ||
| 30 | s64 total_entry_count; | ||
| 31 | s64 last_entry_index; | ||
| 32 | s64 entry_count; | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); | ||
| 35 | |||
| 31 | bool smart_update{}; | 36 | bool smart_update{}; |
| 32 | std::size_t common_offset{}; | 37 | std::size_t common_offset{}; |
| 33 | }; | 38 | }; |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 6ef17acc5..48978e5c6 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -7,72 +7,82 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/settings.h" | 9 | #include "common/settings.h" |
| 10 | #include "core/core.h" | ||
| 10 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 11 | #include "core/frontend/emu_window.h" | 12 | #include "core/frontend/emu_window.h" |
| 12 | #include "core/frontend/input.h" | 13 | #include "core/hid/emulated_console.h" |
| 14 | #include "core/hid/hid_core.h" | ||
| 13 | #include "core/hle/service/hid/controllers/touchscreen.h" | 15 | #include "core/hle/service/hid/controllers/touchscreen.h" |
| 14 | 16 | ||
| 15 | namespace Service::HID { | 17 | namespace Service::HID { |
| 16 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; | 18 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; |
| 17 | 19 | ||
| 18 | Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {} | 20 | Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_) |
| 21 | : ControllerBase{hid_core_} { | ||
| 22 | console = hid_core.GetEmulatedConsole(); | ||
| 23 | } | ||
| 24 | |||
| 19 | Controller_Touchscreen::~Controller_Touchscreen() = default; | 25 | Controller_Touchscreen::~Controller_Touchscreen() = default; |
| 20 | 26 | ||
| 21 | void Controller_Touchscreen::OnInit() { | 27 | void Controller_Touchscreen::OnInit() {} |
| 22 | for (std::size_t id = 0; id < MAX_FINGERS; ++id) { | ||
| 23 | mouse_finger_id[id] = MAX_FINGERS; | ||
| 24 | keyboard_finger_id[id] = MAX_FINGERS; | ||
| 25 | udp_finger_id[id] = MAX_FINGERS; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | 28 | ||
| 29 | void Controller_Touchscreen::OnRelease() {} | 29 | void Controller_Touchscreen::OnRelease() {} |
| 30 | 30 | ||
| 31 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 31 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 32 | std::size_t size) { | 32 | std::size_t size) { |
| 33 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); | 33 | touch_screen_lifo.timestamp = core_timing.GetCPUTicks(); |
| 34 | shared_memory.header.total_entry_count = 17; | ||
| 35 | 34 | ||
| 36 | if (!IsControllerActivated()) { | 35 | if (!IsControllerActivated()) { |
| 37 | shared_memory.header.entry_count = 0; | 36 | touch_screen_lifo.buffer_count = 0; |
| 38 | shared_memory.header.last_entry_index = 0; | 37 | touch_screen_lifo.buffer_tail = 0; |
| 38 | std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo)); | ||
| 39 | return; | 39 | return; |
| 40 | } | 40 | } |
| 41 | shared_memory.header.entry_count = 16; | ||
| 42 | 41 | ||
| 43 | const auto& last_entry = | 42 | const auto touch_status = console->GetTouch(); |
| 44 | shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; | 43 | for (std::size_t id = 0; id < MAX_FINGERS; id++) { |
| 45 | shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; | 44 | const auto& current_touch = touch_status[id]; |
| 46 | auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; | 45 | auto& finger = fingers[id]; |
| 46 | finger.position = current_touch.position; | ||
| 47 | finger.id = current_touch.id; | ||
| 47 | 48 | ||
| 48 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 49 | if (finger.attribute.start_touch) { |
| 49 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 50 | finger.attribute.raw = 0; |
| 51 | continue; | ||
| 52 | } | ||
| 50 | 53 | ||
| 51 | const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); | 54 | if (finger.attribute.end_touch) { |
| 52 | const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); | 55 | finger.attribute.raw = 0; |
| 53 | for (std::size_t id = 0; id < mouse_status.size(); ++id) { | 56 | finger.pressed = false; |
| 54 | mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); | 57 | continue; |
| 55 | udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); | 58 | } |
| 56 | } | 59 | |
| 60 | if (!finger.pressed && current_touch.pressed) { | ||
| 61 | finger.attribute.start_touch.Assign(1); | ||
| 62 | finger.pressed = true; | ||
| 63 | continue; | ||
| 64 | } | ||
| 57 | 65 | ||
| 58 | if (Settings::values.use_touch_from_button) { | 66 | if (finger.pressed && !current_touch.pressed) { |
| 59 | const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus(); | 67 | finger.attribute.raw = 0; |
| 60 | for (std::size_t id = 0; id < mouse_status.size(); ++id) { | 68 | finger.attribute.end_touch.Assign(1); |
| 61 | keyboard_finger_id[id] = | ||
| 62 | UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]); | ||
| 63 | } | 69 | } |
| 64 | } | 70 | } |
| 65 | 71 | ||
| 66 | std::array<Finger, 16> active_fingers; | 72 | std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; |
| 67 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | 73 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), |
| 68 | [](const auto& finger) { return finger.pressed; }); | 74 | [](const auto& finger) { return finger.pressed; }); |
| 69 | const auto active_fingers_count = | 75 | const auto active_fingers_count = |
| 70 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | 76 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); |
| 71 | 77 | ||
| 72 | const u64 tick = core_timing.GetCPUTicks(); | 78 | const u64 tick = core_timing.GetCPUTicks(); |
| 73 | cur_entry.entry_count = static_cast<s32_le>(active_fingers_count); | 79 | const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state; |
| 80 | |||
| 81 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 82 | next_state.entry_count = static_cast<s32>(active_fingers_count); | ||
| 83 | |||
| 74 | for (std::size_t id = 0; id < MAX_FINGERS; ++id) { | 84 | for (std::size_t id = 0; id < MAX_FINGERS; ++id) { |
| 75 | auto& touch_entry = cur_entry.states[id]; | 85 | auto& touch_entry = next_state.states[id]; |
| 76 | if (id < active_fingers_count) { | 86 | if (id < active_fingers_count) { |
| 77 | const auto& [active_x, active_y] = active_fingers[id].position; | 87 | const auto& [active_x, active_y] = active_fingers[id].position; |
| 78 | touch_entry.position = { | 88 | touch_entry.position = { |
| @@ -97,66 +107,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin | |||
| 97 | touch_entry.finger = 0; | 107 | touch_entry.finger = 0; |
| 98 | } | 108 | } |
| 99 | } | 109 | } |
| 100 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory)); | ||
| 101 | } | ||
| 102 | |||
| 103 | void Controller_Touchscreen::OnLoadInputDevices() { | ||
| 104 | touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window"); | ||
| 105 | touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp"); | ||
| 106 | touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); | ||
| 107 | } | ||
| 108 | |||
| 109 | std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const { | ||
| 110 | // Dont assign any touch input to a finger if disabled | ||
| 111 | if (!Settings::values.touchscreen.enabled) { | ||
| 112 | return std::nullopt; | ||
| 113 | } | ||
| 114 | std::size_t first_free_id = 0; | ||
| 115 | while (first_free_id < MAX_FINGERS) { | ||
| 116 | if (!fingers[first_free_id].pressed) { | ||
| 117 | return first_free_id; | ||
| 118 | } else { | ||
| 119 | first_free_id++; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | return std::nullopt; | ||
| 123 | } | ||
| 124 | |||
| 125 | std::size_t Controller_Touchscreen::UpdateTouchInputEvent( | ||
| 126 | const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) { | ||
| 127 | const auto& [x, y, pressed] = touch_input; | ||
| 128 | if (finger_id > MAX_FINGERS) { | ||
| 129 | LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id); | ||
| 130 | return MAX_FINGERS; | ||
| 131 | } | ||
| 132 | if (pressed) { | ||
| 133 | Attributes attribute{}; | ||
| 134 | if (finger_id == MAX_FINGERS) { | ||
| 135 | const auto first_free_id = GetUnusedFingerID(); | ||
| 136 | if (!first_free_id) { | ||
| 137 | // Invalid finger id do nothing | ||
| 138 | return MAX_FINGERS; | ||
| 139 | } | ||
| 140 | finger_id = first_free_id.value(); | ||
| 141 | fingers[finger_id].pressed = true; | ||
| 142 | fingers[finger_id].id = static_cast<u32_le>(finger_id); | ||
| 143 | attribute.start_touch.Assign(1); | ||
| 144 | } | ||
| 145 | fingers[finger_id].position = {x, y}; | ||
| 146 | fingers[finger_id].attribute = attribute; | ||
| 147 | return finger_id; | ||
| 148 | } | ||
| 149 | |||
| 150 | if (finger_id != MAX_FINGERS) { | ||
| 151 | if (!fingers[finger_id].attribute.end_touch) { | ||
| 152 | fingers[finger_id].attribute.end_touch.Assign(1); | ||
| 153 | fingers[finger_id].attribute.start_touch.Assign(0); | ||
| 154 | return finger_id; | ||
| 155 | } | ||
| 156 | fingers[finger_id].pressed = false; | ||
| 157 | } | ||
| 158 | 110 | ||
| 159 | return MAX_FINGERS; | 111 | touch_screen_lifo.WriteNextEntry(next_state); |
| 112 | std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo)); | ||
| 160 | } | 113 | } |
| 161 | 114 | ||
| 162 | } // namespace Service::HID | 115 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 8e9b40c0a..708dde4f0 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h | |||
| @@ -9,18 +9,25 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/point.h" | 10 | #include "common/point.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/frontend/input.h" | 12 | #include "core/hid/hid_types.h" |
| 13 | #include "core/hle/service/hid/controllers/controller_base.h" | 13 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 14 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 15 | |||
| 16 | namespace Core::HID { | ||
| 17 | class EmulatedConsole; | ||
| 18 | } // namespace Core::HID | ||
| 14 | 19 | ||
| 15 | namespace Service::HID { | 20 | namespace Service::HID { |
| 16 | class Controller_Touchscreen final : public ControllerBase { | 21 | class Controller_Touchscreen final : public ControllerBase { |
| 17 | public: | 22 | public: |
| 23 | // This is nn::hid::TouchScreenModeForNx | ||
| 18 | enum class TouchScreenModeForNx : u8 { | 24 | enum class TouchScreenModeForNx : u8 { |
| 19 | UseSystemSetting, | 25 | UseSystemSetting, |
| 20 | Finger, | 26 | Finger, |
| 21 | Heat2, | 27 | Heat2, |
| 22 | }; | 28 | }; |
| 23 | 29 | ||
| 30 | // This is nn::hid::TouchScreenConfigurationForNx | ||
| 24 | struct TouchScreenConfigurationForNx { | 31 | struct TouchScreenConfigurationForNx { |
| 25 | TouchScreenModeForNx mode; | 32 | TouchScreenModeForNx mode; |
| 26 | INSERT_PADDING_BYTES_NOINIT(0x7); | 33 | INSERT_PADDING_BYTES_NOINIT(0x7); |
| @@ -29,7 +36,7 @@ public: | |||
| 29 | static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17, | 36 | static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17, |
| 30 | "TouchScreenConfigurationForNx is an invalid size"); | 37 | "TouchScreenConfigurationForNx is an invalid size"); |
| 31 | 38 | ||
| 32 | explicit Controller_Touchscreen(Core::System& system_); | 39 | explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_); |
| 33 | ~Controller_Touchscreen() override; | 40 | ~Controller_Touchscreen() override; |
| 34 | 41 | ||
| 35 | // Called when the controller is initialized | 42 | // Called when the controller is initialized |
| @@ -41,73 +48,24 @@ public: | |||
| 41 | // When the controller is requesting an update for the shared memory | 48 | // When the controller is requesting an update for the shared memory |
| 42 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; | 49 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; |
| 43 | 50 | ||
| 44 | // Called when input devices should be loaded | ||
| 45 | void OnLoadInputDevices() override; | ||
| 46 | |||
| 47 | private: | 51 | private: |
| 48 | static constexpr std::size_t MAX_FINGERS = 16; | 52 | static constexpr std::size_t MAX_FINGERS = 16; |
| 49 | 53 | ||
| 50 | // Returns an unused finger id, if there is no fingers available std::nullopt will be returned | 54 | // This is nn::hid::TouchScreenState |
| 51 | std::optional<std::size_t> GetUnusedFingerID() const; | 55 | struct TouchScreenState { |
| 52 | 56 | s64 sampling_number; | |
| 53 | // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no | 57 | s32 entry_count; |
| 54 | // changes will be made. Updates the coordinates if the finger id it's already set. If the touch | 58 | INSERT_PADDING_BYTES(4); // Reserved |
| 55 | // ends delays the output by one frame to set the end_touch flag before finally freeing the | 59 | std::array<Core::HID::TouchState, MAX_FINGERS> states; |
| 56 | // finger id | ||
| 57 | std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input, | ||
| 58 | std::size_t finger_id); | ||
| 59 | |||
| 60 | struct Attributes { | ||
| 61 | union { | ||
| 62 | u32 raw{}; | ||
| 63 | BitField<0, 1, u32> start_touch; | ||
| 64 | BitField<1, 1, u32> end_touch; | ||
| 65 | }; | ||
| 66 | }; | ||
| 67 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); | ||
| 68 | |||
| 69 | struct TouchState { | ||
| 70 | u64_le delta_time; | ||
| 71 | Attributes attribute; | ||
| 72 | u32_le finger; | ||
| 73 | Common::Point<u32_le> position; | ||
| 74 | u32_le diameter_x; | ||
| 75 | u32_le diameter_y; | ||
| 76 | u32_le rotation_angle; | ||
| 77 | }; | 60 | }; |
| 78 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | 61 | static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); |
| 79 | 62 | ||
| 80 | struct TouchScreenEntry { | 63 | // This is nn::hid::detail::TouchScreenLifo |
| 81 | s64_le sampling_number; | 64 | Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{}; |
| 82 | s64_le sampling_number2; | 65 | static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); |
| 83 | s32_le entry_count; | 66 | TouchScreenState next_state{}; |
| 84 | std::array<TouchState, MAX_FINGERS> states; | ||
| 85 | }; | ||
| 86 | static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size"); | ||
| 87 | |||
| 88 | struct TouchScreenSharedMemory { | ||
| 89 | CommonHeader header; | ||
| 90 | std::array<TouchScreenEntry, 17> shared_memory_entries{}; | ||
| 91 | INSERT_PADDING_BYTES(0x3c8); | ||
| 92 | }; | ||
| 93 | static_assert(sizeof(TouchScreenSharedMemory) == 0x3000, | ||
| 94 | "TouchScreenSharedMemory is an invalid size"); | ||
| 95 | |||
| 96 | struct Finger { | ||
| 97 | u64_le last_touch{}; | ||
| 98 | Common::Point<float> position; | ||
| 99 | u32_le id{}; | ||
| 100 | bool pressed{}; | ||
| 101 | Attributes attribute; | ||
| 102 | }; | ||
| 103 | 67 | ||
| 104 | TouchScreenSharedMemory shared_memory{}; | 68 | std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers; |
| 105 | std::unique_ptr<Input::TouchDevice> touch_mouse_device; | 69 | Core::HID::EmulatedConsole* console; |
| 106 | std::unique_ptr<Input::TouchDevice> touch_udp_device; | ||
| 107 | std::unique_ptr<Input::TouchDevice> touch_btn_device; | ||
| 108 | std::array<std::size_t, MAX_FINGERS> mouse_finger_id; | ||
| 109 | std::array<std::size_t, MAX_FINGERS> keyboard_finger_id; | ||
| 110 | std::array<std::size_t, MAX_FINGERS> udp_finger_id; | ||
| 111 | std::array<Finger, MAX_FINGERS> fingers; | ||
| 112 | }; | 70 | }; |
| 113 | } // namespace Service::HID | 71 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 41dc22cf9..e4da16466 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp | |||
| @@ -5,12 +5,13 @@ | |||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/hid/hid_core.h" | ||
| 8 | #include "core/hle/service/hid/controllers/xpad.h" | 9 | #include "core/hle/service/hid/controllers/xpad.h" |
| 9 | 10 | ||
| 10 | namespace Service::HID { | 11 | namespace Service::HID { |
| 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; | 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; |
| 12 | 13 | ||
| 13 | Controller_XPad::Controller_XPad(Core::System& system_) : ControllerBase{system_} {} | 14 | Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} |
| 14 | Controller_XPad::~Controller_XPad() = default; | 15 | Controller_XPad::~Controller_XPad() = default; |
| 15 | 16 | ||
| 16 | void Controller_XPad::OnInit() {} | 17 | void Controller_XPad::OnInit() {} |
| @@ -19,28 +20,19 @@ void Controller_XPad::OnRelease() {} | |||
| 19 | 20 | ||
| 20 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 21 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 22 | std::size_t size) { |
| 22 | for (auto& xpad_entry : shared_memory.shared_memory_entries) { | 23 | if (!IsControllerActivated()) { |
| 23 | xpad_entry.header.timestamp = core_timing.GetCPUTicks(); | 24 | basic_xpad_lifo.buffer_count = 0; |
| 24 | xpad_entry.header.total_entry_count = 17; | 25 | basic_xpad_lifo.buffer_tail = 0; |
| 25 | 26 | std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo)); | |
| 26 | if (!IsControllerActivated()) { | 27 | return; |
| 27 | xpad_entry.header.entry_count = 0; | ||
| 28 | xpad_entry.header.last_entry_index = 0; | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | xpad_entry.header.entry_count = 16; | ||
| 32 | |||
| 33 | const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index]; | ||
| 34 | xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17; | ||
| 35 | auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index]; | ||
| 36 | |||
| 37 | cur_entry.sampling_number = last_entry.sampling_number + 1; | ||
| 38 | cur_entry.sampling_number2 = cur_entry.sampling_number; | ||
| 39 | } | 28 | } |
| 29 | |||
| 30 | const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state; | ||
| 31 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 40 | // TODO(ogniK): Update xpad states | 32 | // TODO(ogniK): Update xpad states |
| 41 | 33 | ||
| 42 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); | 34 | basic_xpad_lifo.WriteNextEntry(next_state); |
| 35 | std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo)); | ||
| 43 | } | 36 | } |
| 44 | 37 | ||
| 45 | void Controller_XPad::OnLoadInputDevices() {} | ||
| 46 | } // namespace Service::HID | 38 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index f9ab5facf..ba8db8d9d 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h | |||
| @@ -8,12 +8,14 @@ | |||
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | #include "core/hid/hid_types.h" | ||
| 11 | #include "core/hle/service/hid/controllers/controller_base.h" | 12 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 13 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 12 | 14 | ||
| 13 | namespace Service::HID { | 15 | namespace Service::HID { |
| 14 | class Controller_XPad final : public ControllerBase { | 16 | class Controller_XPad final : public ControllerBase { |
| 15 | public: | 17 | public: |
| 16 | explicit Controller_XPad(Core::System& system_); | 18 | explicit Controller_XPad(Core::HID::HIDCore& hid_core_); |
| 17 | ~Controller_XPad() override; | 19 | ~Controller_XPad() override; |
| 18 | 20 | ||
| 19 | // Called when the controller is initialized | 21 | // Called when the controller is initialized |
| @@ -25,13 +27,11 @@ public: | |||
| 25 | // When the controller is requesting an update for the shared memory | 27 | // When the controller is requesting an update for the shared memory |
| 26 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; | 28 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; |
| 27 | 29 | ||
| 28 | // Called when input devices should be loaded | ||
| 29 | void OnLoadInputDevices() override; | ||
| 30 | |||
| 31 | private: | 30 | private: |
| 32 | struct Attributes { | 31 | // This is nn::hid::BasicXpadAttributeSet |
| 32 | struct BasicXpadAttributeSet { | ||
| 33 | union { | 33 | union { |
| 34 | u32_le raw{}; | 34 | u32 raw{}; |
| 35 | BitField<0, 1, u32> is_connected; | 35 | BitField<0, 1, u32> is_connected; |
| 36 | BitField<1, 1, u32> is_wired; | 36 | BitField<1, 1, u32> is_wired; |
| 37 | BitField<2, 1, u32> is_left_connected; | 37 | BitField<2, 1, u32> is_left_connected; |
| @@ -40,11 +40,12 @@ private: | |||
| 40 | BitField<5, 1, u32> is_right_wired; | 40 | BitField<5, 1, u32> is_right_wired; |
| 41 | }; | 41 | }; |
| 42 | }; | 42 | }; |
| 43 | static_assert(sizeof(Attributes) == 4, "Attributes is an invalid size"); | 43 | static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size"); |
| 44 | 44 | ||
| 45 | struct Buttons { | 45 | // This is nn::hid::BasicXpadButtonSet |
| 46 | struct BasicXpadButtonSet { | ||
| 46 | union { | 47 | union { |
| 47 | u32_le raw{}; | 48 | u32 raw{}; |
| 48 | // Button states | 49 | // Button states |
| 49 | BitField<0, 1, u32> a; | 50 | BitField<0, 1, u32> a; |
| 50 | BitField<1, 1, u32> b; | 51 | BitField<1, 1, u32> b; |
| @@ -88,35 +89,21 @@ private: | |||
| 88 | BitField<30, 1, u32> handheld_left_b; | 89 | BitField<30, 1, u32> handheld_left_b; |
| 89 | }; | 90 | }; |
| 90 | }; | 91 | }; |
| 91 | static_assert(sizeof(Buttons) == 4, "Buttons is an invalid size"); | 92 | static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size"); |
| 92 | 93 | ||
| 93 | struct AnalogStick { | 94 | // This is nn::hid::detail::BasicXpadState |
| 94 | s32_le x; | 95 | struct BasicXpadState { |
| 95 | s32_le y; | 96 | s64 sampling_number; |
| 96 | }; | 97 | BasicXpadAttributeSet attributes; |
| 97 | static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size"); | 98 | BasicXpadButtonSet pad_states; |
| 98 | 99 | Core::HID::AnalogStickState l_stick; | |
| 99 | struct XPadState { | 100 | Core::HID::AnalogStickState r_stick; |
| 100 | s64_le sampling_number; | ||
| 101 | s64_le sampling_number2; | ||
| 102 | Attributes attributes; | ||
| 103 | Buttons pad_states; | ||
| 104 | AnalogStick l_stick; | ||
| 105 | AnalogStick r_stick; | ||
| 106 | }; | 101 | }; |
| 107 | static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size"); | 102 | static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size"); |
| 108 | 103 | ||
| 109 | struct XPadEntry { | 104 | // This is nn::hid::detail::BasicXpadLifo |
| 110 | CommonHeader header; | 105 | Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{}; |
| 111 | std::array<XPadState, 17> pad_states{}; | 106 | static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size"); |
| 112 | INSERT_PADDING_BYTES(0x138); | 107 | BasicXpadState next_state{}; |
| 113 | }; | ||
| 114 | static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size"); | ||
| 115 | |||
| 116 | struct SharedMemory { | ||
| 117 | std::array<XPadEntry, 4> shared_memory_entries{}; | ||
| 118 | }; | ||
| 119 | static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); | ||
| 120 | SharedMemory shared_memory{}; | ||
| 121 | }; | 108 | }; |
| 122 | } // namespace Service::HID | 109 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 10c64d41a..7163e1a4e 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include "common/settings.h" | 8 | #include "common/settings.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/core_timing.h" | 10 | #include "core/core_timing.h" |
| 11 | #include "core/frontend/input.h" | 11 | #include "core/hid/hid_core.h" |
| 12 | #include "core/hle/ipc_helpers.h" | 12 | #include "core/hle/ipc_helpers.h" |
| 13 | #include "core/hle/kernel/k_readable_event.h" | 13 | #include "core/hle/kernel/k_readable_event.h" |
| 14 | #include "core/hle/kernel/k_shared_memory.h" | 14 | #include "core/hle/kernel/k_shared_memory.h" |
| @@ -34,10 +34,10 @@ | |||
| 34 | namespace Service::HID { | 34 | namespace Service::HID { |
| 35 | 35 | ||
| 36 | // Updating period for each HID device. | 36 | // Updating period for each HID device. |
| 37 | // HID is polled every 15ms, this value was derived from | 37 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew |
| 38 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet | 38 | constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) |
| 39 | constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz) | 39 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) |
| 40 | constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz) | 40 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) |
| 41 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 41 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 42 | 42 | ||
| 43 | IAppletResource::IAppletResource(Core::System& system_, | 43 | IAppletResource::IAppletResource(Core::System& system_, |
| @@ -79,17 +79,24 @@ IAppletResource::IAppletResource(Core::System& system_, | |||
| 79 | const auto guard = LockService(); | 79 | const auto guard = LockService(); |
| 80 | UpdateControllers(user_data, ns_late); | 80 | UpdateControllers(user_data, ns_late); |
| 81 | }); | 81 | }); |
| 82 | mouse_keyboard_update_event = Core::Timing::CreateEvent( | ||
| 83 | "HID::UpdateMouseKeyboardCallback", | ||
| 84 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||
| 85 | const auto guard = LockService(); | ||
| 86 | UpdateMouseKeyboard(user_data, ns_late); | ||
| 87 | }); | ||
| 82 | motion_update_event = Core::Timing::CreateEvent( | 88 | motion_update_event = Core::Timing::CreateEvent( |
| 83 | "HID::MotionPadCallback", | 89 | "HID::UpdateMotionCallback", |
| 84 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 90 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 85 | const auto guard = LockService(); | 91 | const auto guard = LockService(); |
| 86 | UpdateMotion(user_data, ns_late); | 92 | UpdateMotion(user_data, ns_late); |
| 87 | }); | 93 | }); |
| 88 | 94 | ||
| 89 | system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); | 95 | system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); |
| 96 | system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event); | ||
| 90 | system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); | 97 | system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); |
| 91 | 98 | ||
| 92 | ReloadInputDevices(); | 99 | system.HIDCore().ReloadInputDevices(); |
| 93 | } | 100 | } |
| 94 | 101 | ||
| 95 | void IAppletResource::ActivateController(HidController controller) { | 102 | void IAppletResource::ActivateController(HidController controller) { |
| @@ -102,6 +109,7 @@ void IAppletResource::DeactivateController(HidController controller) { | |||
| 102 | 109 | ||
| 103 | IAppletResource::~IAppletResource() { | 110 | IAppletResource::~IAppletResource() { |
| 104 | system.CoreTiming().UnscheduleEvent(pad_update_event, 0); | 111 | system.CoreTiming().UnscheduleEvent(pad_update_event, 0); |
| 112 | system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); | ||
| 105 | system.CoreTiming().UnscheduleEvent(motion_update_event, 0); | 113 | system.CoreTiming().UnscheduleEvent(motion_update_event, 0); |
| 106 | } | 114 | } |
| 107 | 115 | ||
| @@ -117,23 +125,44 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, | |||
| 117 | std::chrono::nanoseconds ns_late) { | 125 | std::chrono::nanoseconds ns_late) { |
| 118 | auto& core_timing = system.CoreTiming(); | 126 | auto& core_timing = system.CoreTiming(); |
| 119 | 127 | ||
| 120 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); | ||
| 121 | for (const auto& controller : controllers) { | 128 | for (const auto& controller : controllers) { |
| 122 | if (should_reload) { | 129 | // Keyboard has it's own update event |
| 123 | controller->OnLoadInputDevices(); | 130 | if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) { |
| 131 | continue; | ||
| 132 | } | ||
| 133 | // Mouse has it's own update event | ||
| 134 | if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { | ||
| 135 | continue; | ||
| 124 | } | 136 | } |
| 125 | controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(), | 137 | controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(), |
| 126 | SHARED_MEMORY_SIZE); | 138 | SHARED_MEMORY_SIZE); |
| 127 | } | 139 | } |
| 128 | 140 | ||
| 129 | // If ns_late is higher than the update rate ignore the delay | 141 | // If ns_late is higher than the update rate ignore the delay |
| 130 | if (ns_late > motion_update_ns) { | 142 | if (ns_late > pad_update_ns) { |
| 131 | ns_late = {}; | 143 | ns_late = {}; |
| 132 | } | 144 | } |
| 133 | 145 | ||
| 134 | core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); | 146 | core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); |
| 135 | } | 147 | } |
| 136 | 148 | ||
| 149 | void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, | ||
| 150 | std::chrono::nanoseconds ns_late) { | ||
| 151 | auto& core_timing = system.CoreTiming(); | ||
| 152 | |||
| 153 | controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate( | ||
| 154 | core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); | ||
| 155 | controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate( | ||
| 156 | core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); | ||
| 157 | |||
| 158 | // If ns_late is higher than the update rate ignore the delay | ||
| 159 | if (ns_late > mouse_keyboard_update_ns) { | ||
| 160 | ns_late = {}; | ||
| 161 | } | ||
| 162 | |||
| 163 | core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event); | ||
| 164 | } | ||
| 165 | |||
| 137 | void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 166 | void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 138 | auto& core_timing = system.CoreTiming(); | 167 | auto& core_timing = system.CoreTiming(); |
| 139 | 168 | ||
| @@ -166,7 +195,7 @@ public: | |||
| 166 | private: | 195 | private: |
| 167 | void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) { | 196 | void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) { |
| 168 | IPC::RequestParser rp{ctx}; | 197 | IPC::RequestParser rp{ctx}; |
| 169 | const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; | 198 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; |
| 170 | 199 | ||
| 171 | if (applet_resource != nullptr) { | 200 | if (applet_resource != nullptr) { |
| 172 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 201 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| @@ -264,8 +293,8 @@ Hid::Hid(Core::System& system_) | |||
| 264 | {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, | 293 | {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, |
| 265 | {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, | 294 | {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, |
| 266 | {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, | 295 | {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, |
| 267 | {135, nullptr, "SetNpadCaptureButtonAssignment"}, | 296 | {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"}, |
| 268 | {136, nullptr, "ClearNpadCaptureButtonAssignment"}, | 297 | {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"}, |
| 269 | {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, | 298 | {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, |
| 270 | {201, &Hid::SendVibrationValue, "SendVibrationValue"}, | 299 | {201, &Hid::SendVibrationValue, "SendVibrationValue"}, |
| 271 | {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, | 300 | {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, |
| @@ -422,6 +451,7 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) { | |||
| 422 | INSERT_PADDING_WORDS_NOINIT(1); | 451 | INSERT_PADDING_WORDS_NOINIT(1); |
| 423 | u64 applet_resource_user_id; | 452 | u64 applet_resource_user_id; |
| 424 | }; | 453 | }; |
| 454 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 425 | 455 | ||
| 426 | const auto parameters{rp.PopRaw<Parameters>()}; | 456 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 427 | 457 | ||
| @@ -448,19 +478,18 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { | |||
| 448 | void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { | 478 | void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 449 | IPC::RequestParser rp{ctx}; | 479 | IPC::RequestParser rp{ctx}; |
| 450 | struct Parameters { | 480 | struct Parameters { |
| 451 | Controller_NPad::DeviceHandle sixaxis_handle; | 481 | u32 basic_xpad_id; |
| 452 | INSERT_PADDING_WORDS_NOINIT(1); | 482 | INSERT_PADDING_WORDS_NOINIT(1); |
| 453 | u64 applet_resource_user_id; | 483 | u64 applet_resource_user_id; |
| 454 | }; | 484 | }; |
| 485 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 455 | 486 | ||
| 456 | const auto parameters{rp.PopRaw<Parameters>()}; | 487 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 457 | 488 | ||
| 458 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); | 489 | // This function does nothing on 10.0.0+ |
| 459 | 490 | ||
| 460 | LOG_DEBUG(Service_HID, | 491 | LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", |
| 461 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 492 | parameters.basic_xpad_id, parameters.applet_resource_user_id); |
| 462 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 463 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 464 | 493 | ||
| 465 | IPC::ResponseBuilder rb{ctx, 2}; | 494 | IPC::ResponseBuilder rb{ctx, 2}; |
| 466 | rb.Push(ResultSuccess); | 495 | rb.Push(ResultSuccess); |
| @@ -469,19 +498,18 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 469 | void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { | 498 | void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 470 | IPC::RequestParser rp{ctx}; | 499 | IPC::RequestParser rp{ctx}; |
| 471 | struct Parameters { | 500 | struct Parameters { |
| 472 | Controller_NPad::DeviceHandle sixaxis_handle; | 501 | u32 basic_xpad_id; |
| 473 | INSERT_PADDING_WORDS_NOINIT(1); | 502 | INSERT_PADDING_WORDS_NOINIT(1); |
| 474 | u64 applet_resource_user_id; | 503 | u64 applet_resource_user_id; |
| 475 | }; | 504 | }; |
| 505 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 476 | 506 | ||
| 477 | const auto parameters{rp.PopRaw<Parameters>()}; | 507 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 478 | 508 | ||
| 479 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); | 509 | // This function does nothing on 10.0.0+ |
| 480 | 510 | ||
| 481 | LOG_DEBUG(Service_HID, | 511 | LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", |
| 482 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 512 | parameters.basic_xpad_id, parameters.applet_resource_user_id); |
| 483 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 484 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 485 | 513 | ||
| 486 | IPC::ResponseBuilder rb{ctx, 2}; | 514 | IPC::ResponseBuilder rb{ctx, 2}; |
| 487 | rb.Push(ResultSuccess); | 515 | rb.Push(ResultSuccess); |
| @@ -490,14 +518,16 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 490 | void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { | 518 | void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 491 | IPC::RequestParser rp{ctx}; | 519 | IPC::RequestParser rp{ctx}; |
| 492 | struct Parameters { | 520 | struct Parameters { |
| 493 | Controller_NPad::DeviceHandle sixaxis_handle; | 521 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 494 | INSERT_PADDING_WORDS_NOINIT(1); | 522 | INSERT_PADDING_WORDS_NOINIT(1); |
| 495 | u64 applet_resource_user_id; | 523 | u64 applet_resource_user_id; |
| 496 | }; | 524 | }; |
| 525 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 497 | 526 | ||
| 498 | const auto parameters{rp.PopRaw<Parameters>()}; | 527 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 499 | 528 | ||
| 500 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); | 529 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 530 | .SetSixAxisEnabled(parameters.sixaxis_handle, true); | ||
| 501 | 531 | ||
| 502 | LOG_DEBUG(Service_HID, | 532 | LOG_DEBUG(Service_HID, |
| 503 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 533 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -511,14 +541,16 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 511 | void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { | 541 | void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 512 | IPC::RequestParser rp{ctx}; | 542 | IPC::RequestParser rp{ctx}; |
| 513 | struct Parameters { | 543 | struct Parameters { |
| 514 | Controller_NPad::DeviceHandle sixaxis_handle; | 544 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 515 | INSERT_PADDING_WORDS_NOINIT(1); | 545 | INSERT_PADDING_WORDS_NOINIT(1); |
| 516 | u64 applet_resource_user_id; | 546 | u64 applet_resource_user_id; |
| 517 | }; | 547 | }; |
| 548 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 518 | 549 | ||
| 519 | const auto parameters{rp.PopRaw<Parameters>()}; | 550 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 520 | 551 | ||
| 521 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); | 552 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 553 | .SetSixAxisEnabled(parameters.sixaxis_handle, false); | ||
| 522 | 554 | ||
| 523 | LOG_DEBUG(Service_HID, | 555 | LOG_DEBUG(Service_HID, |
| 524 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 556 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -534,19 +566,23 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { | |||
| 534 | struct Parameters { | 566 | struct Parameters { |
| 535 | bool enable_sixaxis_sensor_fusion; | 567 | bool enable_sixaxis_sensor_fusion; |
| 536 | INSERT_PADDING_BYTES_NOINIT(3); | 568 | INSERT_PADDING_BYTES_NOINIT(3); |
| 537 | Controller_NPad::DeviceHandle sixaxis_handle; | 569 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 538 | u64 applet_resource_user_id; | 570 | u64 applet_resource_user_id; |
| 539 | }; | 571 | }; |
| 540 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | 572 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); |
| 541 | 573 | ||
| 542 | const auto parameters{rp.PopRaw<Parameters>()}; | 574 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 543 | 575 | ||
| 544 | LOG_WARNING(Service_HID, | 576 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 545 | "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " | 577 | .SetSixAxisFusionEnabled(parameters.sixaxis_handle, |
| 546 | "device_index={}, applet_resource_user_id={}", | 578 | parameters.enable_sixaxis_sensor_fusion); |
| 547 | parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, | 579 | |
| 548 | parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, | 580 | LOG_DEBUG(Service_HID, |
| 549 | parameters.applet_resource_user_id); | 581 | "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " |
| 582 | "device_index={}, applet_resource_user_id={}", | ||
| 583 | parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, | ||
| 584 | parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, | ||
| 585 | parameters.applet_resource_user_id); | ||
| 550 | 586 | ||
| 551 | IPC::ResponseBuilder rb{ctx, 2}; | 587 | IPC::ResponseBuilder rb{ctx, 2}; |
| 552 | rb.Push(ResultSuccess); | 588 | rb.Push(ResultSuccess); |
| @@ -555,9 +591,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { | |||
| 555 | void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { | 591 | void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { |
| 556 | IPC::RequestParser rp{ctx}; | 592 | IPC::RequestParser rp{ctx}; |
| 557 | struct Parameters { | 593 | struct Parameters { |
| 558 | Controller_NPad::DeviceHandle sixaxis_handle; | 594 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 559 | f32 parameter1; | 595 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion; |
| 560 | f32 parameter2; | 596 | INSERT_PADDING_WORDS_NOINIT(1); |
| 561 | u64 applet_resource_user_id; | 597 | u64 applet_resource_user_id; |
| 562 | }; | 598 | }; |
| 563 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | 599 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); |
| @@ -565,14 +601,14 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { | |||
| 565 | const auto parameters{rp.PopRaw<Parameters>()}; | 601 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 566 | 602 | ||
| 567 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 603 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 568 | .SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2); | 604 | .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); |
| 569 | 605 | ||
| 570 | LOG_WARNING(Service_HID, | 606 | LOG_DEBUG(Service_HID, |
| 571 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " | 607 | "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " |
| 572 | "parameter2={}, applet_resource_user_id={}", | 608 | "parameter2={}, applet_resource_user_id={}", |
| 573 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | 609 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, |
| 574 | parameters.sixaxis_handle.device_index, parameters.parameter1, | 610 | parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1, |
| 575 | parameters.parameter2, parameters.applet_resource_user_id); | 611 | parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); |
| 576 | 612 | ||
| 577 | IPC::ResponseBuilder rb{ctx, 2}; | 613 | IPC::ResponseBuilder rb{ctx, 2}; |
| 578 | rb.Push(ResultSuccess); | 614 | rb.Push(ResultSuccess); |
| @@ -581,35 +617,33 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { | |||
| 581 | void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { | 617 | void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { |
| 582 | IPC::RequestParser rp{ctx}; | 618 | IPC::RequestParser rp{ctx}; |
| 583 | struct Parameters { | 619 | struct Parameters { |
| 584 | Controller_NPad::DeviceHandle sixaxis_handle; | 620 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 621 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 585 | u64 applet_resource_user_id; | 622 | u64 applet_resource_user_id; |
| 586 | }; | 623 | }; |
| 587 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | 624 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); |
| 588 | 625 | ||
| 589 | f32 parameter1 = 0; | ||
| 590 | f32 parameter2 = 0; | ||
| 591 | const auto parameters{rp.PopRaw<Parameters>()}; | 626 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 592 | 627 | ||
| 593 | std::tie(parameter1, parameter2) = | 628 | const auto sixaxis_fusion_parameters = |
| 594 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 629 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 595 | .GetSixAxisFusionParameters(); | 630 | .GetSixAxisFusionParameters(parameters.sixaxis_handle); |
| 596 | 631 | ||
| 597 | LOG_WARNING( | 632 | LOG_DEBUG(Service_HID, |
| 598 | Service_HID, | 633 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| 599 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 634 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, |
| 600 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | 635 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); |
| 601 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 602 | 636 | ||
| 603 | IPC::ResponseBuilder rb{ctx, 4}; | 637 | IPC::ResponseBuilder rb{ctx, 4}; |
| 604 | rb.Push(ResultSuccess); | 638 | rb.Push(ResultSuccess); |
| 605 | rb.Push(parameter1); | 639 | rb.PushRaw(sixaxis_fusion_parameters); |
| 606 | rb.Push(parameter2); | ||
| 607 | } | 640 | } |
| 608 | 641 | ||
| 609 | void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { | 642 | void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { |
| 610 | IPC::RequestParser rp{ctx}; | 643 | IPC::RequestParser rp{ctx}; |
| 611 | struct Parameters { | 644 | struct Parameters { |
| 612 | Controller_NPad::DeviceHandle sixaxis_handle; | 645 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 646 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 613 | u64 applet_resource_user_id; | 647 | u64 applet_resource_user_id; |
| 614 | }; | 648 | }; |
| 615 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | 649 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); |
| @@ -617,13 +651,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { | |||
| 617 | const auto parameters{rp.PopRaw<Parameters>()}; | 651 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 618 | 652 | ||
| 619 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 653 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 620 | .ResetSixAxisFusionParameters(); | 654 | .ResetSixAxisFusionParameters(parameters.sixaxis_handle); |
| 621 | 655 | ||
| 622 | LOG_WARNING( | 656 | LOG_DEBUG(Service_HID, |
| 623 | Service_HID, | 657 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| 624 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 658 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, |
| 625 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | 659 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); |
| 626 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 627 | 660 | ||
| 628 | IPC::ResponseBuilder rb{ctx, 2}; | 661 | IPC::ResponseBuilder rb{ctx, 2}; |
| 629 | rb.Push(ResultSuccess); | 662 | rb.Push(ResultSuccess); |
| @@ -631,12 +664,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { | |||
| 631 | 664 | ||
| 632 | void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | 665 | void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { |
| 633 | IPC::RequestParser rp{ctx}; | 666 | IPC::RequestParser rp{ctx}; |
| 634 | const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; | 667 | const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()}; |
| 635 | const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; | 668 | const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; |
| 636 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 669 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 637 | 670 | ||
| 638 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 671 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 639 | .SetGyroscopeZeroDriftMode(drift_mode); | 672 | .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); |
| 640 | 673 | ||
| 641 | LOG_DEBUG(Service_HID, | 674 | LOG_DEBUG(Service_HID, |
| 642 | "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " | 675 | "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " |
| @@ -651,10 +684,11 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | |||
| 651 | void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | 684 | void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { |
| 652 | IPC::RequestParser rp{ctx}; | 685 | IPC::RequestParser rp{ctx}; |
| 653 | struct Parameters { | 686 | struct Parameters { |
| 654 | Controller_NPad::DeviceHandle sixaxis_handle; | 687 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 655 | INSERT_PADDING_WORDS_NOINIT(1); | 688 | INSERT_PADDING_WORDS_NOINIT(1); |
| 656 | u64 applet_resource_user_id; | 689 | u64 applet_resource_user_id; |
| 657 | }; | 690 | }; |
| 691 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 658 | 692 | ||
| 659 | const auto parameters{rp.PopRaw<Parameters>()}; | 693 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 660 | 694 | ||
| @@ -666,21 +700,23 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | |||
| 666 | IPC::ResponseBuilder rb{ctx, 3}; | 700 | IPC::ResponseBuilder rb{ctx, 3}; |
| 667 | rb.Push(ResultSuccess); | 701 | rb.Push(ResultSuccess); |
| 668 | rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) | 702 | rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 669 | .GetGyroscopeZeroDriftMode()); | 703 | .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle)); |
| 670 | } | 704 | } |
| 671 | 705 | ||
| 672 | void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | 706 | void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { |
| 673 | IPC::RequestParser rp{ctx}; | 707 | IPC::RequestParser rp{ctx}; |
| 674 | struct Parameters { | 708 | struct Parameters { |
| 675 | Controller_NPad::DeviceHandle sixaxis_handle; | 709 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 676 | INSERT_PADDING_WORDS_NOINIT(1); | 710 | INSERT_PADDING_WORDS_NOINIT(1); |
| 677 | u64 applet_resource_user_id; | 711 | u64 applet_resource_user_id; |
| 678 | }; | 712 | }; |
| 713 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 679 | 714 | ||
| 680 | const auto parameters{rp.PopRaw<Parameters>()}; | 715 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 716 | const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard}; | ||
| 681 | 717 | ||
| 682 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 718 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 683 | .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); | 719 | .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); |
| 684 | 720 | ||
| 685 | LOG_DEBUG(Service_HID, | 721 | LOG_DEBUG(Service_HID, |
| 686 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 722 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -694,10 +730,11 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | |||
| 694 | void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { | 730 | void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { |
| 695 | IPC::RequestParser rp{ctx}; | 731 | IPC::RequestParser rp{ctx}; |
| 696 | struct Parameters { | 732 | struct Parameters { |
| 697 | Controller_NPad::DeviceHandle sixaxis_handle; | 733 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 698 | INSERT_PADDING_WORDS_NOINIT(1); | 734 | INSERT_PADDING_WORDS_NOINIT(1); |
| 699 | u64 applet_resource_user_id; | 735 | u64 applet_resource_user_id; |
| 700 | }; | 736 | }; |
| 737 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 701 | 738 | ||
| 702 | const auto parameters{rp.PopRaw<Parameters>()}; | 739 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 703 | 740 | ||
| @@ -709,16 +746,17 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { | |||
| 709 | IPC::ResponseBuilder rb{ctx, 3}; | 746 | IPC::ResponseBuilder rb{ctx, 3}; |
| 710 | rb.Push(ResultSuccess); | 747 | rb.Push(ResultSuccess); |
| 711 | rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) | 748 | rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 712 | .IsSixAxisSensorAtRest()); | 749 | .IsSixAxisSensorAtRest(parameters.sixaxis_handle)); |
| 713 | } | 750 | } |
| 714 | 751 | ||
| 715 | void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { | 752 | void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 716 | IPC::RequestParser rp{ctx}; | 753 | IPC::RequestParser rp{ctx}; |
| 717 | struct Parameters { | 754 | struct Parameters { |
| 718 | Controller_NPad::DeviceHandle sixaxis_handle; | 755 | Core::HID::SixAxisSensorHandle sixaxis_handle; |
| 719 | INSERT_PADDING_WORDS_NOINIT(1); | 756 | INSERT_PADDING_WORDS_NOINIT(1); |
| 720 | u64 applet_resource_user_id; | 757 | u64 applet_resource_user_id; |
| 721 | }; | 758 | }; |
| 759 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 722 | 760 | ||
| 723 | const auto parameters{rp.PopRaw<Parameters>()}; | 761 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 724 | 762 | ||
| @@ -740,13 +778,14 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { | |||
| 740 | INSERT_PADDING_WORDS_NOINIT(1); | 778 | INSERT_PADDING_WORDS_NOINIT(1); |
| 741 | u64 applet_resource_user_id; | 779 | u64 applet_resource_user_id; |
| 742 | }; | 780 | }; |
| 781 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 743 | 782 | ||
| 744 | const auto parameters{rp.PopRaw<Parameters>()}; | 783 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 745 | 784 | ||
| 746 | applet_resource->ActivateController(HidController::Gesture); | 785 | applet_resource->ActivateController(HidController::Gesture); |
| 747 | 786 | ||
| 748 | LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, | 787 | LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", |
| 749 | parameters.applet_resource_user_id); | 788 | parameters.unknown, parameters.applet_resource_user_id); |
| 750 | 789 | ||
| 751 | IPC::ResponseBuilder rb{ctx, 2}; | 790 | IPC::ResponseBuilder rb{ctx, 2}; |
| 752 | rb.Push(ResultSuccess); | 791 | rb.Push(ResultSuccess); |
| @@ -754,12 +793,20 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { | |||
| 754 | 793 | ||
| 755 | void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { | 794 | void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { |
| 756 | IPC::RequestParser rp{ctx}; | 795 | IPC::RequestParser rp{ctx}; |
| 757 | const auto supported_styleset{rp.Pop<u32>()}; | 796 | struct Parameters { |
| 797 | Core::HID::NpadStyleSet supported_styleset; | ||
| 798 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 799 | u64 applet_resource_user_id; | ||
| 800 | }; | ||
| 801 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 802 | |||
| 803 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 758 | 804 | ||
| 759 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 805 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 760 | .SetSupportedStyleSet({supported_styleset}); | 806 | .SetSupportedStyleSet({parameters.supported_styleset}); |
| 761 | 807 | ||
| 762 | LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); | 808 | LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", |
| 809 | parameters.supported_styleset, parameters.applet_resource_user_id); | ||
| 763 | 810 | ||
| 764 | IPC::ResponseBuilder rb{ctx, 2}; | 811 | IPC::ResponseBuilder rb{ctx, 2}; |
| 765 | rb.Push(ResultSuccess); | 812 | rb.Push(ResultSuccess); |
| @@ -773,9 +820,9 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { | |||
| 773 | 820 | ||
| 774 | IPC::ResponseBuilder rb{ctx, 3}; | 821 | IPC::ResponseBuilder rb{ctx, 3}; |
| 775 | rb.Push(ResultSuccess); | 822 | rb.Push(ResultSuccess); |
| 776 | rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) | 823 | rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 777 | .GetSupportedStyleSet() | 824 | .GetSupportedStyleSet() |
| 778 | .raw); | 825 | .raw); |
| 779 | } | 826 | } |
| 780 | 827 | ||
| 781 | void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { | 828 | void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { |
| @@ -818,11 +865,12 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) { | |||
| 818 | void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { | 865 | void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { |
| 819 | IPC::RequestParser rp{ctx}; | 866 | IPC::RequestParser rp{ctx}; |
| 820 | struct Parameters { | 867 | struct Parameters { |
| 821 | u32 npad_id; | 868 | Core::HID::NpadIdType npad_id; |
| 822 | INSERT_PADDING_WORDS_NOINIT(1); | 869 | INSERT_PADDING_WORDS_NOINIT(1); |
| 823 | u64 applet_resource_user_id; | 870 | u64 applet_resource_user_id; |
| 824 | u64 unknown; | 871 | u64 unknown; |
| 825 | }; | 872 | }; |
| 873 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 826 | 874 | ||
| 827 | const auto parameters{rp.PopRaw<Parameters>()}; | 875 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 828 | 876 | ||
| @@ -838,10 +886,11 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { | |||
| 838 | void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { | 886 | void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { |
| 839 | IPC::RequestParser rp{ctx}; | 887 | IPC::RequestParser rp{ctx}; |
| 840 | struct Parameters { | 888 | struct Parameters { |
| 841 | u32 npad_id; | 889 | Core::HID::NpadIdType npad_id; |
| 842 | INSERT_PADDING_WORDS_NOINIT(1); | 890 | INSERT_PADDING_WORDS_NOINIT(1); |
| 843 | u64 applet_resource_user_id; | 891 | u64 applet_resource_user_id; |
| 844 | }; | 892 | }; |
| 893 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 845 | 894 | ||
| 846 | const auto parameters{rp.PopRaw<Parameters>()}; | 895 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 847 | 896 | ||
| @@ -857,7 +906,7 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { | |||
| 857 | 906 | ||
| 858 | void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { | 907 | void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { |
| 859 | IPC::RequestParser rp{ctx}; | 908 | IPC::RequestParser rp{ctx}; |
| 860 | const auto npad_id{rp.Pop<u32>()}; | 909 | const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 861 | 910 | ||
| 862 | LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); | 911 | LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); |
| 863 | 912 | ||
| @@ -872,16 +921,17 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { | |||
| 872 | // Should have no effect with how our npad sets up the data | 921 | // Should have no effect with how our npad sets up the data |
| 873 | IPC::RequestParser rp{ctx}; | 922 | IPC::RequestParser rp{ctx}; |
| 874 | struct Parameters { | 923 | struct Parameters { |
| 875 | u32 unknown; | 924 | s32 revision; |
| 876 | INSERT_PADDING_WORDS_NOINIT(1); | 925 | INSERT_PADDING_WORDS_NOINIT(1); |
| 877 | u64 applet_resource_user_id; | 926 | u64 applet_resource_user_id; |
| 878 | }; | 927 | }; |
| 928 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 879 | 929 | ||
| 880 | const auto parameters{rp.PopRaw<Parameters>()}; | 930 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 881 | 931 | ||
| 882 | applet_resource->ActivateController(HidController::NPad); | 932 | applet_resource->ActivateController(HidController::NPad); |
| 883 | 933 | ||
| 884 | LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, | 934 | LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, |
| 885 | parameters.applet_resource_user_id); | 935 | parameters.applet_resource_user_id); |
| 886 | 936 | ||
| 887 | IPC::ResponseBuilder rb{ctx, 2}; | 937 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -891,7 +941,7 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { | |||
| 891 | void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { | 941 | void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { |
| 892 | IPC::RequestParser rp{ctx}; | 942 | IPC::RequestParser rp{ctx}; |
| 893 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 943 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 894 | const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()}; | 944 | const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()}; |
| 895 | 945 | ||
| 896 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type); | 946 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type); |
| 897 | 947 | ||
| @@ -916,42 +966,44 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { | |||
| 916 | void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { | 966 | void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { |
| 917 | IPC::RequestParser rp{ctx}; | 967 | IPC::RequestParser rp{ctx}; |
| 918 | struct Parameters { | 968 | struct Parameters { |
| 919 | u32 npad_id; | 969 | Core::HID::NpadIdType npad_id; |
| 920 | INSERT_PADDING_WORDS_NOINIT(1); | 970 | INSERT_PADDING_WORDS_NOINIT(1); |
| 921 | u64 applet_resource_user_id; | 971 | u64 applet_resource_user_id; |
| 922 | }; | 972 | }; |
| 973 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 923 | 974 | ||
| 924 | const auto parameters{rp.PopRaw<Parameters>()}; | 975 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 925 | 976 | ||
| 926 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 977 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 927 | .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); | 978 | .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, |
| 979 | Controller_NPad::NpadJoyAssignmentMode::Single); | ||
| 928 | 980 | ||
| 929 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | 981 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, |
| 930 | parameters.npad_id, parameters.applet_resource_user_id); | 982 | parameters.applet_resource_user_id); |
| 931 | 983 | ||
| 932 | IPC::ResponseBuilder rb{ctx, 2}; | 984 | IPC::ResponseBuilder rb{ctx, 2}; |
| 933 | rb.Push(ResultSuccess); | 985 | rb.Push(ResultSuccess); |
| 934 | } | 986 | } |
| 935 | 987 | ||
| 936 | void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { | 988 | void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { |
| 937 | // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault | ||
| 938 | IPC::RequestParser rp{ctx}; | 989 | IPC::RequestParser rp{ctx}; |
| 939 | struct Parameters { | 990 | struct Parameters { |
| 940 | u32 npad_id; | 991 | Core::HID::NpadIdType npad_id; |
| 941 | INSERT_PADDING_WORDS_NOINIT(1); | 992 | INSERT_PADDING_WORDS_NOINIT(1); |
| 942 | u64 applet_resource_user_id; | 993 | u64 applet_resource_user_id; |
| 943 | u64 npad_joy_device_type; | 994 | Controller_NPad::NpadJoyDeviceType npad_joy_device_type; |
| 944 | }; | 995 | }; |
| 996 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 945 | 997 | ||
| 946 | const auto parameters{rp.PopRaw<Parameters>()}; | 998 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 947 | 999 | ||
| 948 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 1000 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 949 | .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); | 1001 | .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, |
| 1002 | Controller_NPad::NpadJoyAssignmentMode::Single); | ||
| 950 | 1003 | ||
| 951 | LOG_WARNING(Service_HID, | 1004 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", |
| 952 | "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", | 1005 | parameters.npad_id, parameters.applet_resource_user_id, |
| 953 | parameters.npad_id, parameters.applet_resource_user_id, | 1006 | parameters.npad_joy_device_type); |
| 954 | parameters.npad_joy_device_type); | ||
| 955 | 1007 | ||
| 956 | IPC::ResponseBuilder rb{ctx, 2}; | 1008 | IPC::ResponseBuilder rb{ctx, 2}; |
| 957 | rb.Push(ResultSuccess); | 1009 | rb.Push(ResultSuccess); |
| @@ -960,18 +1012,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { | |||
| 960 | void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { | 1012 | void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { |
| 961 | IPC::RequestParser rp{ctx}; | 1013 | IPC::RequestParser rp{ctx}; |
| 962 | struct Parameters { | 1014 | struct Parameters { |
| 963 | u32 npad_id; | 1015 | Core::HID::NpadIdType npad_id; |
| 964 | INSERT_PADDING_WORDS_NOINIT(1); | 1016 | INSERT_PADDING_WORDS_NOINIT(1); |
| 965 | u64 applet_resource_user_id; | 1017 | u64 applet_resource_user_id; |
| 966 | }; | 1018 | }; |
| 1019 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 967 | 1020 | ||
| 968 | const auto parameters{rp.PopRaw<Parameters>()}; | 1021 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 969 | 1022 | ||
| 970 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 1023 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 971 | .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual); | 1024 | .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); |
| 972 | 1025 | ||
| 973 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | 1026 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, |
| 974 | parameters.npad_id, parameters.applet_resource_user_id); | 1027 | parameters.applet_resource_user_id); |
| 975 | 1028 | ||
| 976 | IPC::ResponseBuilder rb{ctx, 2}; | 1029 | IPC::ResponseBuilder rb{ctx, 2}; |
| 977 | rb.Push(ResultSuccess); | 1030 | rb.Push(ResultSuccess); |
| @@ -979,8 +1032,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { | |||
| 979 | 1032 | ||
| 980 | void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { | 1033 | void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { |
| 981 | IPC::RequestParser rp{ctx}; | 1034 | IPC::RequestParser rp{ctx}; |
| 982 | const auto npad_id_1{rp.Pop<u32>()}; | 1035 | const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 983 | const auto npad_id_2{rp.Pop<u32>()}; | 1036 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 984 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1037 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 985 | 1038 | ||
| 986 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 1039 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| @@ -1046,8 +1099,8 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { | |||
| 1046 | 1099 | ||
| 1047 | void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { | 1100 | void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { |
| 1048 | IPC::RequestParser rp{ctx}; | 1101 | IPC::RequestParser rp{ctx}; |
| 1049 | const auto npad_id_1{rp.Pop<u32>()}; | 1102 | const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 1050 | const auto npad_id_2{rp.Pop<u32>()}; | 1103 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 1051 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1104 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1052 | 1105 | ||
| 1053 | const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) | 1106 | const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| @@ -1068,10 +1121,11 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { | |||
| 1068 | void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { | 1121 | void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { |
| 1069 | IPC::RequestParser rp{ctx}; | 1122 | IPC::RequestParser rp{ctx}; |
| 1070 | struct Parameters { | 1123 | struct Parameters { |
| 1071 | u32 npad_id; | 1124 | Core::HID::NpadIdType npad_id; |
| 1072 | INSERT_PADDING_WORDS_NOINIT(1); | 1125 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1073 | u64 applet_resource_user_id; | 1126 | u64 applet_resource_user_id; |
| 1074 | }; | 1127 | }; |
| 1128 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1075 | 1129 | ||
| 1076 | const auto parameters{rp.PopRaw<Parameters>()}; | 1130 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1077 | 1131 | ||
| @@ -1089,9 +1143,10 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c | |||
| 1089 | struct Parameters { | 1143 | struct Parameters { |
| 1090 | bool unintended_home_button_input_protection; | 1144 | bool unintended_home_button_input_protection; |
| 1091 | INSERT_PADDING_BYTES_NOINIT(3); | 1145 | INSERT_PADDING_BYTES_NOINIT(3); |
| 1092 | u32 npad_id; | 1146 | Core::HID::NpadIdType npad_id; |
| 1093 | u64 applet_resource_user_id; | 1147 | u64 applet_resource_user_id; |
| 1094 | }; | 1148 | }; |
| 1149 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1095 | 1150 | ||
| 1096 | const auto parameters{rp.PopRaw<Parameters>()}; | 1151 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1097 | 1152 | ||
| @@ -1113,6 +1168,7 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) { | |||
| 1113 | IPC::RequestParser rp{ctx}; | 1168 | IPC::RequestParser rp{ctx}; |
| 1114 | struct Parameters { | 1169 | struct Parameters { |
| 1115 | bool analog_stick_use_center_clamp; | 1170 | bool analog_stick_use_center_clamp; |
| 1171 | INSERT_PADDING_BYTES_NOINIT(7); | ||
| 1116 | u64 applet_resource_user_id; | 1172 | u64 applet_resource_user_id; |
| 1117 | }; | 1173 | }; |
| 1118 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | 1174 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); |
| @@ -1130,40 +1186,71 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) { | |||
| 1130 | rb.Push(ResultSuccess); | 1186 | rb.Push(ResultSuccess); |
| 1131 | } | 1187 | } |
| 1132 | 1188 | ||
| 1189 | void Hid::SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) { | ||
| 1190 | IPC::RequestParser rp{ctx}; | ||
| 1191 | struct Parameters { | ||
| 1192 | Core::HID::NpadStyleSet npad_styleset; | ||
| 1193 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1194 | u64 applet_resource_user_id; | ||
| 1195 | Core::HID::NpadButton button; | ||
| 1196 | }; | ||
| 1197 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1198 | |||
| 1199 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1200 | |||
| 1201 | LOG_WARNING(Service_HID, | ||
| 1202 | "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", | ||
| 1203 | parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); | ||
| 1204 | |||
| 1205 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1206 | rb.Push(ResultSuccess); | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) { | ||
| 1210 | IPC::RequestParser rp{ctx}; | ||
| 1211 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1212 | |||
| 1213 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1214 | applet_resource_user_id); | ||
| 1215 | |||
| 1216 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1217 | rb.Push(ResultSuccess); | ||
| 1218 | } | ||
| 1219 | |||
| 1133 | void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { | 1220 | void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { |
| 1134 | IPC::RequestParser rp{ctx}; | 1221 | IPC::RequestParser rp{ctx}; |
| 1135 | const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; | 1222 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; |
| 1136 | 1223 | ||
| 1137 | VibrationDeviceInfo vibration_device_info; | 1224 | Core::HID::VibrationDeviceInfo vibration_device_info; |
| 1138 | 1225 | ||
| 1139 | switch (vibration_device_handle.npad_type) { | 1226 | switch (vibration_device_handle.npad_type) { |
| 1140 | case Controller_NPad::NpadType::ProController: | 1227 | case Core::HID::NpadStyleIndex::ProController: |
| 1141 | case Controller_NPad::NpadType::Handheld: | 1228 | case Core::HID::NpadStyleIndex::Handheld: |
| 1142 | case Controller_NPad::NpadType::JoyconDual: | 1229 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 1143 | case Controller_NPad::NpadType::JoyconLeft: | 1230 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1144 | case Controller_NPad::NpadType::JoyconRight: | 1231 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1145 | default: | 1232 | default: |
| 1146 | vibration_device_info.type = VibrationDeviceType::LinearResonantActuator; | 1233 | vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; |
| 1147 | break; | 1234 | break; |
| 1148 | case Controller_NPad::NpadType::GameCube: | 1235 | case Core::HID::NpadStyleIndex::GameCube: |
| 1149 | vibration_device_info.type = VibrationDeviceType::GcErm; | 1236 | vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; |
| 1150 | break; | 1237 | break; |
| 1151 | case Controller_NPad::NpadType::Pokeball: | 1238 | case Core::HID::NpadStyleIndex::Pokeball: |
| 1152 | vibration_device_info.type = VibrationDeviceType::Unknown; | 1239 | vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; |
| 1153 | break; | 1240 | break; |
| 1154 | } | 1241 | } |
| 1155 | 1242 | ||
| 1156 | switch (vibration_device_handle.device_index) { | 1243 | switch (vibration_device_handle.device_index) { |
| 1157 | case Controller_NPad::DeviceIndex::Left: | 1244 | case Core::HID::DeviceIndex::Left: |
| 1158 | vibration_device_info.position = VibrationDevicePosition::Left; | 1245 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; |
| 1159 | break; | 1246 | break; |
| 1160 | case Controller_NPad::DeviceIndex::Right: | 1247 | case Core::HID::DeviceIndex::Right: |
| 1161 | vibration_device_info.position = VibrationDevicePosition::Right; | 1248 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; |
| 1162 | break; | 1249 | break; |
| 1163 | case Controller_NPad::DeviceIndex::None: | 1250 | case Core::HID::DeviceIndex::None: |
| 1164 | default: | 1251 | default: |
| 1165 | UNREACHABLE_MSG("DeviceIndex should never be None!"); | 1252 | UNREACHABLE_MSG("DeviceIndex should never be None!"); |
| 1166 | vibration_device_info.position = VibrationDevicePosition::None; | 1253 | vibration_device_info.position = Core::HID::VibrationDevicePosition::None; |
| 1167 | break; | 1254 | break; |
| 1168 | } | 1255 | } |
| 1169 | 1256 | ||
| @@ -1178,11 +1265,12 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { | |||
| 1178 | void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { | 1265 | void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { |
| 1179 | IPC::RequestParser rp{ctx}; | 1266 | IPC::RequestParser rp{ctx}; |
| 1180 | struct Parameters { | 1267 | struct Parameters { |
| 1181 | Controller_NPad::DeviceHandle vibration_device_handle; | 1268 | Core::HID::VibrationDeviceHandle vibration_device_handle; |
| 1182 | Controller_NPad::VibrationValue vibration_value; | 1269 | Core::HID::VibrationValue vibration_value; |
| 1183 | INSERT_PADDING_WORDS_NOINIT(1); | 1270 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1184 | u64 applet_resource_user_id; | 1271 | u64 applet_resource_user_id; |
| 1185 | }; | 1272 | }; |
| 1273 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 1186 | 1274 | ||
| 1187 | const auto parameters{rp.PopRaw<Parameters>()}; | 1275 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1188 | 1276 | ||
| @@ -1202,10 +1290,11 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { | |||
| 1202 | void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { | 1290 | void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { |
| 1203 | IPC::RequestParser rp{ctx}; | 1291 | IPC::RequestParser rp{ctx}; |
| 1204 | struct Parameters { | 1292 | struct Parameters { |
| 1205 | Controller_NPad::DeviceHandle vibration_device_handle; | 1293 | Core::HID::VibrationDeviceHandle vibration_device_handle; |
| 1206 | INSERT_PADDING_WORDS_NOINIT(1); | 1294 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1207 | u64 applet_resource_user_id; | 1295 | u64 applet_resource_user_id; |
| 1208 | }; | 1296 | }; |
| 1297 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1209 | 1298 | ||
| 1210 | const auto parameters{rp.PopRaw<Parameters>()}; | 1299 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1211 | 1300 | ||
| @@ -1256,10 +1345,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { | |||
| 1256 | const auto handles = ctx.ReadBuffer(0); | 1345 | const auto handles = ctx.ReadBuffer(0); |
| 1257 | const auto vibrations = ctx.ReadBuffer(1); | 1346 | const auto vibrations = ctx.ReadBuffer(1); |
| 1258 | 1347 | ||
| 1259 | std::vector<Controller_NPad::DeviceHandle> vibration_device_handles( | 1348 | std::vector<Core::HID::VibrationDeviceHandle> vibration_device_handles( |
| 1260 | handles.size() / sizeof(Controller_NPad::DeviceHandle)); | 1349 | handles.size() / sizeof(Core::HID::VibrationDeviceHandle)); |
| 1261 | std::vector<Controller_NPad::VibrationValue> vibration_values( | 1350 | std::vector<Core::HID::VibrationValue> vibration_values(vibrations.size() / |
| 1262 | vibrations.size() / sizeof(Controller_NPad::VibrationValue)); | 1351 | sizeof(Core::HID::VibrationValue)); |
| 1263 | 1352 | ||
| 1264 | std::memcpy(vibration_device_handles.data(), handles.data(), handles.size()); | 1353 | std::memcpy(vibration_device_handles.data(), handles.data(), handles.size()); |
| 1265 | std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size()); | 1354 | std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size()); |
| @@ -1276,9 +1365,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { | |||
| 1276 | void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { | 1365 | void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { |
| 1277 | IPC::RequestParser rp{ctx}; | 1366 | IPC::RequestParser rp{ctx}; |
| 1278 | struct Parameters { | 1367 | struct Parameters { |
| 1279 | Controller_NPad::DeviceHandle vibration_device_handle; | 1368 | Core::HID::VibrationDeviceHandle vibration_device_handle; |
| 1369 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1280 | u64 applet_resource_user_id; | 1370 | u64 applet_resource_user_id; |
| 1281 | VibrationGcErmCommand gc_erm_command; | 1371 | Core::HID::VibrationGcErmCommand gc_erm_command; |
| 1282 | }; | 1372 | }; |
| 1283 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | 1373 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); |
| 1284 | 1374 | ||
| @@ -1292,26 +1382,26 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { | |||
| 1292 | */ | 1382 | */ |
| 1293 | const auto vibration_value = [parameters] { | 1383 | const auto vibration_value = [parameters] { |
| 1294 | switch (parameters.gc_erm_command) { | 1384 | switch (parameters.gc_erm_command) { |
| 1295 | case VibrationGcErmCommand::Stop: | 1385 | case Core::HID::VibrationGcErmCommand::Stop: |
| 1296 | return Controller_NPad::VibrationValue{ | 1386 | return Core::HID::VibrationValue{ |
| 1297 | .amp_low = 0.0f, | 1387 | .low_amplitude = 0.0f, |
| 1298 | .freq_low = 160.0f, | 1388 | .low_frequency = 160.0f, |
| 1299 | .amp_high = 0.0f, | 1389 | .high_amplitude = 0.0f, |
| 1300 | .freq_high = 320.0f, | 1390 | .high_frequency = 320.0f, |
| 1301 | }; | 1391 | }; |
| 1302 | case VibrationGcErmCommand::Start: | 1392 | case Core::HID::VibrationGcErmCommand::Start: |
| 1303 | return Controller_NPad::VibrationValue{ | 1393 | return Core::HID::VibrationValue{ |
| 1304 | .amp_low = 1.0f, | 1394 | .low_amplitude = 1.0f, |
| 1305 | .freq_low = 160.0f, | 1395 | .low_frequency = 160.0f, |
| 1306 | .amp_high = 1.0f, | 1396 | .high_amplitude = 1.0f, |
| 1307 | .freq_high = 320.0f, | 1397 | .high_frequency = 320.0f, |
| 1308 | }; | 1398 | }; |
| 1309 | case VibrationGcErmCommand::StopHard: | 1399 | case Core::HID::VibrationGcErmCommand::StopHard: |
| 1310 | return Controller_NPad::VibrationValue{ | 1400 | return Core::HID::VibrationValue{ |
| 1311 | .amp_low = 0.0f, | 1401 | .low_amplitude = 0.0f, |
| 1312 | .freq_low = 0.0f, | 1402 | .low_frequency = 0.0f, |
| 1313 | .amp_high = 0.0f, | 1403 | .high_amplitude = 0.0f, |
| 1314 | .freq_high = 0.0f, | 1404 | .high_frequency = 0.0f, |
| 1315 | }; | 1405 | }; |
| 1316 | default: | 1406 | default: |
| 1317 | return Controller_NPad::DEFAULT_VIBRATION_VALUE; | 1407 | return Controller_NPad::DEFAULT_VIBRATION_VALUE; |
| @@ -1336,7 +1426,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { | |||
| 1336 | void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { | 1426 | void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { |
| 1337 | IPC::RequestParser rp{ctx}; | 1427 | IPC::RequestParser rp{ctx}; |
| 1338 | struct Parameters { | 1428 | struct Parameters { |
| 1339 | Controller_NPad::DeviceHandle vibration_device_handle; | 1429 | Core::HID::VibrationDeviceHandle vibration_device_handle; |
| 1340 | INSERT_PADDING_WORDS_NOINIT(1); | 1430 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1341 | u64 applet_resource_user_id; | 1431 | u64 applet_resource_user_id; |
| 1342 | }; | 1432 | }; |
| @@ -1347,8 +1437,8 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { | |||
| 1347 | .GetLastVibration(parameters.vibration_device_handle); | 1437 | .GetLastVibration(parameters.vibration_device_handle); |
| 1348 | 1438 | ||
| 1349 | const auto gc_erm_command = [last_vibration] { | 1439 | const auto gc_erm_command = [last_vibration] { |
| 1350 | if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) { | 1440 | if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { |
| 1351 | return VibrationGcErmCommand::Start; | 1441 | return Core::HID::VibrationGcErmCommand::Start; |
| 1352 | } | 1442 | } |
| 1353 | 1443 | ||
| 1354 | /** | 1444 | /** |
| @@ -1357,11 +1447,11 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { | |||
| 1357 | * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. | 1447 | * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. |
| 1358 | * This is done to reuse the controller vibration functions made for regular controllers. | 1448 | * This is done to reuse the controller vibration functions made for regular controllers. |
| 1359 | */ | 1449 | */ |
| 1360 | if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) { | 1450 | if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) { |
| 1361 | return VibrationGcErmCommand::StopHard; | 1451 | return Core::HID::VibrationGcErmCommand::StopHard; |
| 1362 | } | 1452 | } |
| 1363 | 1453 | ||
| 1364 | return VibrationGcErmCommand::Stop; | 1454 | return Core::HID::VibrationGcErmCommand::Stop; |
| 1365 | }(); | 1455 | }(); |
| 1366 | 1456 | ||
| 1367 | LOG_DEBUG(Service_HID, | 1457 | LOG_DEBUG(Service_HID, |
| @@ -1401,10 +1491,11 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { | |||
| 1401 | void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { | 1491 | void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { |
| 1402 | IPC::RequestParser rp{ctx}; | 1492 | IPC::RequestParser rp{ctx}; |
| 1403 | struct Parameters { | 1493 | struct Parameters { |
| 1404 | Controller_NPad::DeviceHandle vibration_device_handle; | 1494 | Core::HID::VibrationDeviceHandle vibration_device_handle; |
| 1405 | INSERT_PADDING_WORDS_NOINIT(1); | 1495 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1406 | u64 applet_resource_user_id; | 1496 | u64 applet_resource_user_id; |
| 1407 | }; | 1497 | }; |
| 1498 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1408 | 1499 | ||
| 1409 | const auto parameters{rp.PopRaw<Parameters>()}; | 1500 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1410 | 1501 | ||
| @@ -1435,18 +1526,18 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 1435 | void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | 1526 | void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 1436 | IPC::RequestParser rp{ctx}; | 1527 | IPC::RequestParser rp{ctx}; |
| 1437 | struct Parameters { | 1528 | struct Parameters { |
| 1438 | Controller_NPad::DeviceHandle sixaxis_handle; | 1529 | Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; |
| 1439 | INSERT_PADDING_WORDS_NOINIT(1); | 1530 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1440 | u64 applet_resource_user_id; | 1531 | u64 applet_resource_user_id; |
| 1441 | }; | 1532 | }; |
| 1533 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1442 | 1534 | ||
| 1443 | const auto parameters{rp.PopRaw<Parameters>()}; | 1535 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1444 | 1536 | ||
| 1445 | LOG_WARNING( | 1537 | LOG_WARNING(Service_HID, |
| 1446 | Service_HID, | 1538 | "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", |
| 1447 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 1539 | parameters.console_sixaxis_handle.unknown_1, |
| 1448 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | 1540 | parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); |
| 1449 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 1450 | 1541 | ||
| 1451 | IPC::ResponseBuilder rb{ctx, 2}; | 1542 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1452 | rb.Push(ResultSuccess); | 1543 | rb.Push(ResultSuccess); |
| @@ -1455,18 +1546,18 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 1455 | void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | 1546 | void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 1456 | IPC::RequestParser rp{ctx}; | 1547 | IPC::RequestParser rp{ctx}; |
| 1457 | struct Parameters { | 1548 | struct Parameters { |
| 1458 | Controller_NPad::DeviceHandle sixaxis_handle; | 1549 | Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; |
| 1459 | INSERT_PADDING_WORDS_NOINIT(1); | 1550 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1460 | u64 applet_resource_user_id; | 1551 | u64 applet_resource_user_id; |
| 1461 | }; | 1552 | }; |
| 1553 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1462 | 1554 | ||
| 1463 | const auto parameters{rp.PopRaw<Parameters>()}; | 1555 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1464 | 1556 | ||
| 1465 | LOG_WARNING( | 1557 | LOG_WARNING(Service_HID, |
| 1466 | Service_HID, | 1558 | "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", |
| 1467 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 1559 | parameters.console_sixaxis_handle.unknown_1, |
| 1468 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | 1560 | parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); |
| 1469 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 1470 | 1561 | ||
| 1471 | IPC::ResponseBuilder rb{ctx, 2}; | 1562 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1472 | rb.Push(ResultSuccess); | 1563 | rb.Push(ResultSuccess); |
| @@ -1620,10 +1711,8 @@ void Hid::SetNpadCommunicationMode(Kernel::HLERequestContext& ctx) { | |||
| 1620 | 1711 | ||
| 1621 | void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) { | 1712 | void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) { |
| 1622 | IPC::RequestParser rp{ctx}; | 1713 | IPC::RequestParser rp{ctx}; |
| 1623 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1624 | 1714 | ||
| 1625 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | 1715 | LOG_WARNING(Service_HID, "(STUBBED) called"); |
| 1626 | applet_resource_user_id); | ||
| 1627 | 1716 | ||
| 1628 | IPC::ResponseBuilder rb{ctx, 4}; | 1717 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1629 | rb.Push(ResultSuccess); | 1718 | rb.Push(ResultSuccess); |
| @@ -1825,7 +1914,7 @@ public: | |||
| 1825 | {317, nullptr, "GetNpadLeftRightInterfaceType"}, | 1914 | {317, nullptr, "GetNpadLeftRightInterfaceType"}, |
| 1826 | {318, nullptr, "HasBattery"}, | 1915 | {318, nullptr, "HasBattery"}, |
| 1827 | {319, nullptr, "HasLeftRightBattery"}, | 1916 | {319, nullptr, "HasLeftRightBattery"}, |
| 1828 | {321, nullptr, "GetUniquePadsFromNpad"}, | 1917 | {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, |
| 1829 | {322, nullptr, "GetIrSensorState"}, | 1918 | {322, nullptr, "GetIrSensorState"}, |
| 1830 | {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, | 1919 | {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, |
| 1831 | {324, nullptr, "GetUniquePadButtonSet"}, | 1920 | {324, nullptr, "GetUniquePadButtonSet"}, |
| @@ -1996,6 +2085,18 @@ private: | |||
| 1996 | IPC::ResponseBuilder rb{ctx, 2}; | 2085 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1997 | rb.Push(ResultSuccess); | 2086 | rb.Push(ResultSuccess); |
| 1998 | } | 2087 | } |
| 2088 | |||
| 2089 | void GetUniquePadsFromNpad(Kernel::HLERequestContext& ctx) { | ||
| 2090 | IPC::RequestParser rp{ctx}; | ||
| 2091 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 2092 | |||
| 2093 | const s64 total_entries = 0; | ||
| 2094 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); | ||
| 2095 | |||
| 2096 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 2097 | rb.Push(ResultSuccess); | ||
| 2098 | rb.Push(total_entries); | ||
| 2099 | } | ||
| 1999 | }; | 2100 | }; |
| 2000 | 2101 | ||
| 2001 | class HidTmp final : public ServiceFramework<HidTmp> { | 2102 | class HidTmp final : public ServiceFramework<HidTmp> { |
| @@ -2037,10 +2138,6 @@ public: | |||
| 2037 | } | 2138 | } |
| 2038 | }; | 2139 | }; |
| 2039 | 2140 | ||
| 2040 | void ReloadInputDevices() { | ||
| 2041 | Settings::values.is_device_reload_pending.store(true); | ||
| 2042 | } | ||
| 2043 | |||
| 2044 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { | 2141 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 2045 | std::make_shared<Hid>(system)->InstallAsService(service_manager); | 2142 | std::make_shared<Hid>(system)->InstallAsService(service_manager); |
| 2046 | std::make_shared<HidBus>(system)->InstallAsService(service_manager); | 2143 | std::make_shared<HidBus>(system)->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index b1fe75e94..d290df161 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -60,21 +60,23 @@ public: | |||
| 60 | private: | 60 | private: |
| 61 | template <typename T> | 61 | template <typename T> |
| 62 | void MakeController(HidController controller) { | 62 | void MakeController(HidController controller) { |
| 63 | controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); | 63 | controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore()); |
| 64 | } | 64 | } |
| 65 | template <typename T> | 65 | template <typename T> |
| 66 | void MakeControllerWithServiceContext(HidController controller) { | 66 | void MakeControllerWithServiceContext(HidController controller) { |
| 67 | controllers[static_cast<std::size_t>(controller)] = | 67 | controllers[static_cast<std::size_t>(controller)] = |
| 68 | std::make_unique<T>(system, service_context); | 68 | std::make_unique<T>(system.HIDCore(), service_context); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); | 71 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); |
| 72 | void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | 72 | void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 73 | void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 73 | void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | 74 | void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 74 | 75 | ||
| 75 | KernelHelpers::ServiceContext& service_context; | 76 | KernelHelpers::ServiceContext& service_context; |
| 76 | 77 | ||
| 77 | std::shared_ptr<Core::Timing::EventType> pad_update_event; | 78 | std::shared_ptr<Core::Timing::EventType> pad_update_event; |
| 79 | std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; | ||
| 78 | std::shared_ptr<Core::Timing::EventType> motion_update_event; | 80 | std::shared_ptr<Core::Timing::EventType> motion_update_event; |
| 79 | 81 | ||
| 80 | std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> | 82 | std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> |
| @@ -134,6 +136,8 @@ private: | |||
| 134 | void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); | 136 | void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); |
| 135 | void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); | 137 | void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); |
| 136 | void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx); | 138 | void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx); |
| 139 | void SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx); | ||
| 140 | void ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx); | ||
| 137 | void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); | 141 | void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); |
| 138 | void SendVibrationValue(Kernel::HLERequestContext& ctx); | 142 | void SendVibrationValue(Kernel::HLERequestContext& ctx); |
| 139 | void GetActualVibrationValue(Kernel::HLERequestContext& ctx); | 143 | void GetActualVibrationValue(Kernel::HLERequestContext& ctx); |
| @@ -161,38 +165,11 @@ private: | |||
| 161 | void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); | 165 | void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); |
| 162 | void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); | 166 | void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); |
| 163 | 167 | ||
| 164 | enum class VibrationDeviceType : u32 { | ||
| 165 | Unknown = 0, | ||
| 166 | LinearResonantActuator = 1, | ||
| 167 | GcErm = 2, | ||
| 168 | }; | ||
| 169 | |||
| 170 | enum class VibrationDevicePosition : u32 { | ||
| 171 | None = 0, | ||
| 172 | Left = 1, | ||
| 173 | Right = 2, | ||
| 174 | }; | ||
| 175 | |||
| 176 | enum class VibrationGcErmCommand : u64 { | ||
| 177 | Stop = 0, | ||
| 178 | Start = 1, | ||
| 179 | StopHard = 2, | ||
| 180 | }; | ||
| 181 | |||
| 182 | struct VibrationDeviceInfo { | ||
| 183 | VibrationDeviceType type{}; | ||
| 184 | VibrationDevicePosition position{}; | ||
| 185 | }; | ||
| 186 | static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); | ||
| 187 | |||
| 188 | std::shared_ptr<IAppletResource> applet_resource; | 168 | std::shared_ptr<IAppletResource> applet_resource; |
| 189 | 169 | ||
| 190 | KernelHelpers::ServiceContext service_context; | 170 | KernelHelpers::ServiceContext service_context; |
| 191 | }; | 171 | }; |
| 192 | 172 | ||
| 193 | /// Reload input devices. Used when input configuration changed | ||
| 194 | void ReloadInputDevices(); | ||
| 195 | |||
| 196 | /// Registers all HID services with the specified service manager. | 173 | /// Registers all HID services with the specified service manager. |
| 197 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 174 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 198 | 175 | ||
diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h new file mode 100644 index 000000000..44c20d967 --- /dev/null +++ b/src/core/hle/service/hid/ring_lifo.h | |||
| @@ -0,0 +1,54 @@ | |||
| 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 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Service::HID { | ||
| 12 | |||
| 13 | template <typename State> | ||
| 14 | struct AtomicStorage { | ||
| 15 | s64 sampling_number; | ||
| 16 | State state; | ||
| 17 | }; | ||
| 18 | |||
| 19 | template <typename State, std::size_t max_buffer_size> | ||
| 20 | struct Lifo { | ||
| 21 | s64 timestamp{}; | ||
| 22 | s64 total_buffer_count = static_cast<s64>(max_buffer_size); | ||
| 23 | s64 buffer_tail{}; | ||
| 24 | s64 buffer_count{}; | ||
| 25 | std::array<AtomicStorage<State>, max_buffer_size> entries{}; | ||
| 26 | |||
| 27 | const AtomicStorage<State>& ReadCurrentEntry() const { | ||
| 28 | return entries[buffer_tail]; | ||
| 29 | } | ||
| 30 | |||
| 31 | const AtomicStorage<State>& ReadPreviousEntry() const { | ||
| 32 | return entries[GetPreviousEntryIndex()]; | ||
| 33 | } | ||
| 34 | |||
| 35 | std::size_t GetPreviousEntryIndex() const { | ||
| 36 | return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count); | ||
| 37 | } | ||
| 38 | |||
| 39 | std::size_t GetNextEntryIndex() const { | ||
| 40 | return static_cast<size_t>((buffer_tail + 1) % total_buffer_count); | ||
| 41 | } | ||
| 42 | |||
| 43 | void WriteNextEntry(const State& new_state) { | ||
| 44 | if (buffer_count < total_buffer_count - 1) { | ||
| 45 | buffer_count++; | ||
| 46 | } | ||
| 47 | buffer_tail = GetNextEntryIndex(); | ||
| 48 | const auto& previous_entry = ReadPreviousEntry(); | ||
| 49 | entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1; | ||
| 50 | entries[buffer_tail].state = new_state; | ||
| 51 | } | ||
| 52 | }; | ||
| 53 | |||
| 54 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 32eff3b2a..3782703d2 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -396,12 +396,12 @@ public: | |||
| 396 | CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, | 396 | CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, |
| 397 | nro_header.segment_headers[DATA_INDEX].memory_size); | 397 | nro_header.segment_headers[DATA_INDEX].memory_size); |
| 398 | 398 | ||
| 399 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( | 399 | CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( |
| 400 | text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute)); | 400 | text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute)); |
| 401 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(ro_start, data_start - ro_start, | 401 | CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( |
| 402 | Kernel::KMemoryPermission::Read)); | 402 | ro_start, data_start - ro_start, Kernel::KMemoryPermission::Read)); |
| 403 | 403 | ||
| 404 | return process->PageTable().SetCodeMemoryPermission( | 404 | return process->PageTable().SetProcessMemoryPermission( |
| 405 | data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite); | 405 | data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite); |
| 406 | } | 406 | } |
| 407 | 407 | ||
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 64ffc8572..382ddcae5 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/hle/service/ns/errors.h" | 12 | #include "core/hle/service/ns/errors.h" |
| 13 | #include "core/hle/service/ns/language.h" | 13 | #include "core/hle/service/ns/language.h" |
| 14 | #include "core/hle/service/ns/ns.h" | 14 | #include "core/hle/service/ns/ns.h" |
| 15 | #include "core/hle/service/ns/pdm_qry.h" | ||
| 15 | #include "core/hle/service/ns/pl_u.h" | 16 | #include "core/hle/service/ns/pl_u.h" |
| 16 | #include "core/hle/service/set/set.h" | 17 | #include "core/hle/service/set/set.h" |
| 17 | 18 | ||
| @@ -570,11 +571,29 @@ IFactoryResetInterface::IFactoryResetInterface(Core::System& system_) | |||
| 570 | 571 | ||
| 571 | IFactoryResetInterface::~IFactoryResetInterface() = default; | 572 | IFactoryResetInterface::~IFactoryResetInterface() = default; |
| 572 | 573 | ||
| 574 | IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface( | ||
| 575 | Core::System& system_) | ||
| 576 | : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { | ||
| 577 | // clang-format off | ||
| 578 | static const FunctionInfo functions[] = { | ||
| 579 | {0, nullptr, "GetApplicationControlData"}, | ||
| 580 | {1, nullptr, "GetApplicationDesiredLanguage"}, | ||
| 581 | {2, nullptr, "ConvertApplicationLanguageToLanguageCode"}, | ||
| 582 | {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, | ||
| 583 | {4, nullptr, "SelectApplicationDesiredLanguage"}, | ||
| 584 | }; | ||
| 585 | // clang-format on | ||
| 586 | |||
| 587 | RegisterHandlers(functions); | ||
| 588 | } | ||
| 589 | |||
| 590 | IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default; | ||
| 591 | |||
| 573 | NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { | 592 | NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { |
| 574 | // clang-format off | 593 | // clang-format off |
| 575 | static const FunctionInfo functions[] = { | 594 | static const FunctionInfo functions[] = { |
| 576 | {7988, nullptr, "GetDynamicRightsInterface"}, | 595 | {7988, nullptr, "GetDynamicRightsInterface"}, |
| 577 | {7989, nullptr, "GetReadOnlyApplicationControlDataInterface"}, | 596 | {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"}, |
| 578 | {7991, nullptr, "GetReadOnlyApplicationRecordInterface"}, | 597 | {7991, nullptr, "GetReadOnlyApplicationRecordInterface"}, |
| 579 | {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, | 598 | {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, |
| 580 | {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, | 599 | {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, |
| @@ -738,6 +757,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system | |||
| 738 | std::make_shared<NS_SU>(system)->InstallAsService(service_manager); | 757 | std::make_shared<NS_SU>(system)->InstallAsService(service_manager); |
| 739 | std::make_shared<NS_VM>(system)->InstallAsService(service_manager); | 758 | std::make_shared<NS_VM>(system)->InstallAsService(service_manager); |
| 740 | 759 | ||
| 760 | std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager); | ||
| 761 | |||
| 741 | std::make_shared<PL_U>(system)->InstallAsService(service_manager); | 762 | std::make_shared<PL_U>(system)->InstallAsService(service_manager); |
| 742 | } | 763 | } |
| 743 | 764 | ||
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 218eec3ec..43540b0fb 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h | |||
| @@ -74,6 +74,13 @@ public: | |||
| 74 | ~IFactoryResetInterface() override; | 74 | ~IFactoryResetInterface() override; |
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | class IReadOnlyApplicationControlDataInterface final | ||
| 78 | : public ServiceFramework<IReadOnlyApplicationControlDataInterface> { | ||
| 79 | public: | ||
| 80 | explicit IReadOnlyApplicationControlDataInterface(Core::System& system_); | ||
| 81 | ~IReadOnlyApplicationControlDataInterface() override; | ||
| 82 | }; | ||
| 83 | |||
| 77 | class NS final : public ServiceFramework<NS> { | 84 | class NS final : public ServiceFramework<NS> { |
| 78 | public: | 85 | public: |
| 79 | explicit NS(const char* name, Core::System& system_); | 86 | explicit NS(const char* name, Core::System& system_); |
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp new file mode 100644 index 000000000..e2fab5c3f --- /dev/null +++ b/src/core/hle/service/ns/pdm_qry.cpp | |||
| @@ -0,0 +1,69 @@ | |||
| 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 <memory> | ||
| 6 | |||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "common/uuid.h" | ||
| 9 | #include "core/hle/ipc_helpers.h" | ||
| 10 | #include "core/hle/service/ns/pdm_qry.h" | ||
| 11 | #include "core/hle/service/service.h" | ||
| 12 | #include "core/hle/service/sm/sm.h" | ||
| 13 | |||
| 14 | namespace Service::NS { | ||
| 15 | |||
| 16 | PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} { | ||
| 17 | // clang-format off | ||
| 18 | static const FunctionInfo functions[] = { | ||
| 19 | {0, nullptr, "QueryAppletEvent"}, | ||
| 20 | {1, nullptr, "QueryPlayStatistics"}, | ||
| 21 | {2, nullptr, "QueryPlayStatisticsByUserAccountId"}, | ||
| 22 | {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"}, | ||
| 23 | {4, nullptr, "QueryPlayStatisticsByApplicationId"}, | ||
| 24 | {5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"}, | ||
| 25 | {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"}, | ||
| 26 | {7, nullptr, "QueryLastPlayTimeV0"}, | ||
| 27 | {8, nullptr, "QueryPlayEvent"}, | ||
| 28 | {9, nullptr, "GetAvailablePlayEventRange"}, | ||
| 29 | {10, nullptr, "QueryAccountEvent"}, | ||
| 30 | {11, nullptr, "QueryAccountPlayEvent"}, | ||
| 31 | {12, nullptr, "GetAvailableAccountPlayEventRange"}, | ||
| 32 | {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"}, | ||
| 33 | {14, nullptr, "QueryRecentlyPlayedApplication"}, | ||
| 34 | {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"}, | ||
| 35 | {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"}, | ||
| 36 | {17, nullptr, "QueryLastPlayTime"}, | ||
| 37 | {18, nullptr, "QueryApplicationPlayStatisticsForSystem"}, | ||
| 38 | {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"}, | ||
| 39 | }; | ||
| 40 | // clang-format on | ||
| 41 | |||
| 42 | RegisterHandlers(functions); | ||
| 43 | } | ||
| 44 | |||
| 45 | PDM_QRY::~PDM_QRY() = default; | ||
| 46 | |||
| 47 | void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx) { | ||
| 48 | IPC::RequestParser rp{ctx}; | ||
| 49 | const auto unknown = rp.Pop<bool>(); | ||
| 50 | rp.Pop<u8>(); // Padding | ||
| 51 | const auto application_id = rp.Pop<u64>(); | ||
| 52 | const auto user_account_uid = rp.PopRaw<Common::UUID>(); | ||
| 53 | |||
| 54 | // TODO(German77): Read statistics of the game | ||
| 55 | PlayStatistics statistics{ | ||
| 56 | .application_id = application_id, | ||
| 57 | .total_launches = 1, | ||
| 58 | }; | ||
| 59 | |||
| 60 | LOG_WARNING(Service_NS, | ||
| 61 | "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}", | ||
| 62 | unknown, application_id, user_account_uid.Format()); | ||
| 63 | |||
| 64 | IPC::ResponseBuilder rb{ctx, 12}; | ||
| 65 | rb.Push(ResultSuccess); | ||
| 66 | rb.PushRaw(statistics); | ||
| 67 | } | ||
| 68 | |||
| 69 | } // namespace Service::NS | ||
diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h new file mode 100644 index 000000000..516136314 --- /dev/null +++ b/src/core/hle/service/ns/pdm_qry.h | |||
| @@ -0,0 +1,33 @@ | |||
| 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 "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Service::NS { | ||
| 10 | |||
| 11 | struct PlayStatistics { | ||
| 12 | u64 application_id{}; | ||
| 13 | u32 first_entry_index{}; | ||
| 14 | u32 first_timestamp_user{}; | ||
| 15 | u32 first_timestamp_network{}; | ||
| 16 | u32 last_entry_index{}; | ||
| 17 | u32 last_timestamp_user{}; | ||
| 18 | u32 last_timestamp_network{}; | ||
| 19 | u32 play_time_in_minutes{}; | ||
| 20 | u32 total_launches{}; | ||
| 21 | }; | ||
| 22 | static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size"); | ||
| 23 | |||
| 24 | class PDM_QRY final : public ServiceFramework<PDM_QRY> { | ||
| 25 | public: | ||
| 26 | explicit PDM_QRY(Core::System& system_); | ||
| 27 | ~PDM_QRY() override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | void QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx); | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace Service::NS | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 0d7d4ad03..8314d1ec2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp | |||
| @@ -20,8 +20,12 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& | |||
| 20 | switch (command.group) { | 20 | switch (command.group) { |
| 21 | case 0x0: | 21 | case 0x0: |
| 22 | switch (command.cmd) { | 22 | switch (command.cmd) { |
| 23 | case 0x1: | 23 | case 0x1: { |
| 24 | return Submit(input, output); | 24 | if (!fd_to_id.contains(fd)) { |
| 25 | fd_to_id[fd] = next_id++; | ||
| 26 | } | ||
| 27 | return Submit(fd, input, output); | ||
| 28 | } | ||
| 25 | case 0x2: | 29 | case 0x2: |
| 26 | return GetSyncpoint(input, output); | 30 | return GetSyncpoint(input, output); |
| 27 | case 0x3: | 31 | case 0x3: |
| @@ -66,7 +70,10 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {} | |||
| 66 | 70 | ||
| 67 | void nvhost_nvdec::OnClose(DeviceFD fd) { | 71 | void nvhost_nvdec::OnClose(DeviceFD fd) { |
| 68 | LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); | 72 | LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); |
| 69 | system.GPU().ClearCdmaInstance(); | 73 | const auto iter = fd_to_id.find(fd); |
| 74 | if (iter != fd_to_id.end()) { | ||
| 75 | system.GPU().ClearCdmaInstance(iter->second); | ||
| 76 | } | ||
| 70 | } | 77 | } |
| 71 | 78 | ||
| 72 | } // namespace Service::Nvidia::Devices | 79 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 523d96e3a..a507c4d0a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h | |||
| @@ -24,6 +24,9 @@ public: | |||
| 24 | 24 | ||
| 25 | void OnOpen(DeviceFD fd) override; | 25 | void OnOpen(DeviceFD fd) override; |
| 26 | void OnClose(DeviceFD fd) override; | 26 | void OnClose(DeviceFD fd) override; |
| 27 | |||
| 28 | private: | ||
| 29 | u32 next_id{}; | ||
| 27 | }; | 30 | }; |
| 28 | 31 | ||
| 29 | } // namespace Service::Nvidia::Devices | 32 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index e61261f98..8a05f0668 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp | |||
| @@ -59,7 +59,8 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { | |||
| 59 | return NvResult::Success; | 59 | return NvResult::Success; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) { | 62 | NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input, |
| 63 | std::vector<u8>& output) { | ||
| 63 | IoctlSubmit params{}; | 64 | IoctlSubmit params{}; |
| 64 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); | 65 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); |
| 65 | LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); | 66 | LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); |
| @@ -93,7 +94,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u | |||
| 93 | Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); | 94 | Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); |
| 94 | system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), | 95 | system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), |
| 95 | cmdlist.size() * sizeof(u32)); | 96 | cmdlist.size() * sizeof(u32)); |
| 96 | gpu.PushCommandBuffer(cmdlist); | 97 | gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); |
| 97 | } | 98 | } |
| 98 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); | 99 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); |
| 99 | // Some games expect command_buffers to be written back | 100 | // Some games expect command_buffers to be written back |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 351625c17..e28c54df6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h | |||
| @@ -104,13 +104,14 @@ protected: | |||
| 104 | 104 | ||
| 105 | /// Ioctl command implementations | 105 | /// Ioctl command implementations |
| 106 | NvResult SetNVMAPfd(const std::vector<u8>& input); | 106 | NvResult SetNVMAPfd(const std::vector<u8>& input); |
| 107 | NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output); | 107 | NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output); |
| 108 | NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); | 108 | NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); |
| 109 | NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); | 109 | NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); |
| 110 | NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); | 110 | NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); |
| 111 | NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); | 111 | NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); |
| 112 | NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); | 112 | NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); |
| 113 | 113 | ||
| 114 | std::unordered_map<DeviceFD, u32> fd_to_id{}; | ||
| 114 | s32_le nvmap_fd{}; | 115 | s32_le nvmap_fd{}; |
| 115 | u32_le submit_timeout{}; | 116 | u32_le submit_timeout{}; |
| 116 | std::shared_ptr<nvmap> nvmap_dev; | 117 | std::shared_ptr<nvmap> nvmap_dev; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index eac4dd530..76b39806f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp | |||
| @@ -21,7 +21,10 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i | |||
| 21 | case 0x0: | 21 | case 0x0: |
| 22 | switch (command.cmd) { | 22 | switch (command.cmd) { |
| 23 | case 0x1: | 23 | case 0x1: |
| 24 | return Submit(input, output); | 24 | if (!fd_to_id.contains(fd)) { |
| 25 | fd_to_id[fd] = next_id++; | ||
| 26 | } | ||
| 27 | return Submit(fd, input, output); | ||
| 25 | case 0x2: | 28 | case 0x2: |
| 26 | return GetSyncpoint(input, output); | 29 | return GetSyncpoint(input, output); |
| 27 | case 0x3: | 30 | case 0x3: |
| @@ -65,7 +68,10 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i | |||
| 65 | void nvhost_vic::OnOpen(DeviceFD fd) {} | 68 | void nvhost_vic::OnOpen(DeviceFD fd) {} |
| 66 | 69 | ||
| 67 | void nvhost_vic::OnClose(DeviceFD fd) { | 70 | void nvhost_vic::OnClose(DeviceFD fd) { |
| 68 | system.GPU().ClearCdmaInstance(); | 71 | const auto iter = fd_to_id.find(fd); |
| 72 | if (iter != fd_to_id.end()) { | ||
| 73 | system.GPU().ClearCdmaInstance(iter->second); | ||
| 74 | } | ||
| 69 | } | 75 | } |
| 70 | 76 | ||
| 71 | } // namespace Service::Nvidia::Devices | 77 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index 6d7fda9d1..c9732c037 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h | |||
| @@ -23,5 +23,8 @@ public: | |||
| 23 | 23 | ||
| 24 | void OnOpen(DeviceFD fd) override; | 24 | void OnOpen(DeviceFD fd) override; |
| 25 | void OnClose(DeviceFD fd) override; | 25 | void OnClose(DeviceFD fd) override; |
| 26 | |||
| 27 | private: | ||
| 28 | u32 next_id{}; | ||
| 26 | }; | 29 | }; |
| 27 | } // namespace Service::Nvidia::Devices | 30 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 3294bc0e7..5ab221fc1 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #pragma once | 5 | #pragma once |
| 2 | 6 | ||
| 3 | #include <array> | 7 | #include <array> |
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index 88fc5b5cc..277abc17a 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp | |||
| @@ -13,7 +13,12 @@ namespace Service::PM { | |||
| 13 | 13 | ||
| 14 | namespace { | 14 | namespace { |
| 15 | 15 | ||
| 16 | constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1}; | 16 | constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1}; |
| 17 | [[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2}; | ||
| 18 | [[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3}; | ||
| 19 | [[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4}; | ||
| 20 | [[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5}; | ||
| 21 | [[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6}; | ||
| 17 | 22 | ||
| 18 | constexpr u64 NO_PROCESS_FOUND_PID{0}; | 23 | constexpr u64 NO_PROCESS_FOUND_PID{0}; |
| 19 | 24 | ||
| @@ -95,18 +100,18 @@ public: | |||
| 95 | private: | 100 | private: |
| 96 | void GetProcessId(Kernel::HLERequestContext& ctx) { | 101 | void GetProcessId(Kernel::HLERequestContext& ctx) { |
| 97 | IPC::RequestParser rp{ctx}; | 102 | IPC::RequestParser rp{ctx}; |
| 98 | const auto title_id = rp.PopRaw<u64>(); | 103 | const auto program_id = rp.PopRaw<u64>(); |
| 99 | 104 | ||
| 100 | LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id); | 105 | LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); |
| 101 | 106 | ||
| 102 | const auto process = | 107 | const auto process = |
| 103 | SearchProcessList(kernel.GetProcessList(), [title_id](const auto& proc) { | 108 | SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) { |
| 104 | return proc->GetProgramID() == title_id; | 109 | return proc->GetProgramID() == program_id; |
| 105 | }); | 110 | }); |
| 106 | 111 | ||
| 107 | if (!process.has_value()) { | 112 | if (!process.has_value()) { |
| 108 | IPC::ResponseBuilder rb{ctx, 2}; | 113 | IPC::ResponseBuilder rb{ctx, 2}; |
| 109 | rb.Push(ERROR_PROCESS_NOT_FOUND); | 114 | rb.Push(ResultProcessNotFound); |
| 110 | return; | 115 | return; |
| 111 | } | 116 | } |
| 112 | 117 | ||
| @@ -128,13 +133,16 @@ public: | |||
| 128 | explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_) | 133 | explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_) |
| 129 | : ServiceFramework{system_, "pm:info"}, process_list{process_list_} { | 134 | : ServiceFramework{system_, "pm:info"}, process_list{process_list_} { |
| 130 | static const FunctionInfo functions[] = { | 135 | static const FunctionInfo functions[] = { |
| 131 | {0, &Info::GetTitleId, "GetTitleId"}, | 136 | {0, &Info::GetProgramId, "GetProgramId"}, |
| 137 | {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"}, | ||
| 138 | {65001, nullptr, "AtmosphereHasLaunchedProgram"}, | ||
| 139 | {65002, nullptr, "AtmosphereGetProcessInfo"}, | ||
| 132 | }; | 140 | }; |
| 133 | RegisterHandlers(functions); | 141 | RegisterHandlers(functions); |
| 134 | } | 142 | } |
| 135 | 143 | ||
| 136 | private: | 144 | private: |
| 137 | void GetTitleId(Kernel::HLERequestContext& ctx) { | 145 | void GetProgramId(Kernel::HLERequestContext& ctx) { |
| 138 | IPC::RequestParser rp{ctx}; | 146 | IPC::RequestParser rp{ctx}; |
| 139 | const auto process_id = rp.PopRaw<u64>(); | 147 | const auto process_id = rp.PopRaw<u64>(); |
| 140 | 148 | ||
| @@ -146,7 +154,7 @@ private: | |||
| 146 | 154 | ||
| 147 | if (!process.has_value()) { | 155 | if (!process.has_value()) { |
| 148 | IPC::ResponseBuilder rb{ctx, 2}; | 156 | IPC::ResponseBuilder rb{ctx, 2}; |
| 149 | rb.Push(ERROR_PROCESS_NOT_FOUND); | 157 | rb.Push(ResultProcessNotFound); |
| 150 | return; | 158 | return; |
| 151 | } | 159 | } |
| 152 | 160 | ||
| @@ -155,6 +163,27 @@ private: | |||
| 155 | rb.Push((*process)->GetProgramID()); | 163 | rb.Push((*process)->GetProgramID()); |
| 156 | } | 164 | } |
| 157 | 165 | ||
| 166 | void AtmosphereGetProcessId(Kernel::HLERequestContext& ctx) { | ||
| 167 | IPC::RequestParser rp{ctx}; | ||
| 168 | const auto program_id = rp.PopRaw<u64>(); | ||
| 169 | |||
| 170 | LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); | ||
| 171 | |||
| 172 | const auto process = SearchProcessList(process_list, [program_id](const auto& proc) { | ||
| 173 | return proc->GetProgramID() == program_id; | ||
| 174 | }); | ||
| 175 | |||
| 176 | if (!process.has_value()) { | ||
| 177 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 178 | rb.Push(ResultProcessNotFound); | ||
| 179 | return; | ||
| 180 | } | ||
| 181 | |||
| 182 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 183 | rb.Push(ResultSuccess); | ||
| 184 | rb.Push((*process)->GetProcessID()); | ||
| 185 | } | ||
| 186 | |||
| 158 | const std::vector<Kernel::KProcess*>& process_list; | 187 | const std::vector<Kernel::KProcess*>& process_list; |
| 159 | }; | 188 | }; |
| 160 | 189 | ||
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 1b5aca65d..b47e3bf69 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -125,8 +125,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 125 | } | 125 | } |
| 126 | metadata.Print(); | 126 | metadata.Print(); |
| 127 | 127 | ||
| 128 | const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 128 | const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", |
| 129 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}; | 129 | "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", |
| 130 | "subsdk8", "subsdk9", "sdk"}; | ||
| 130 | 131 | ||
| 131 | // Use the NSO module loader to figure out the code layout | 132 | // Use the NSO module loader to figure out the code layout |
| 132 | std::size_t code_size{}; | 133 | std::size_t code_size{}; |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 20f0e90f5..12446c9ac 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -19,7 +19,6 @@ | |||
| 19 | namespace Core::Memory { | 19 | namespace Core::Memory { |
| 20 | namespace { | 20 | namespace { |
| 21 | constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; | 21 | constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; |
| 22 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | ||
| 23 | 22 | ||
| 24 | std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) { | 23 | std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) { |
| 25 | auto end_index = start_index; | 24 | auto end_index = start_index; |
| @@ -61,7 +60,7 @@ u64 StandardVmCallbacks::HidKeysDown() { | |||
| 61 | applet_resource | 60 | applet_resource |
| 62 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) | 61 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) |
| 63 | .GetAndResetPressState(); | 62 | .GetAndResetPressState(); |
| 64 | return press_state & KEYPAD_BITMASK; | 63 | return static_cast<u64>(press_state & HID::NpadButton::All); |
| 65 | } | 64 | } |
| 66 | 65 | ||
| 67 | void StandardVmCallbacks::DebugLog(u8 id, u64 value) { | 66 | void StandardVmCallbacks::DebugLog(u8 id, u64 value) { |
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index a2541906f..816202588 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h | |||
| @@ -33,7 +33,7 @@ public: | |||
| 33 | explicit PerfStats(u64 title_id_); | 33 | explicit PerfStats(u64 title_id_); |
| 34 | ~PerfStats(); | 34 | ~PerfStats(); |
| 35 | 35 | ||
| 36 | using Clock = std::chrono::high_resolution_clock; | 36 | using Clock = std::chrono::steady_clock; |
| 37 | 37 | ||
| 38 | void BeginSystemFrame(); | 38 | void BeginSystemFrame(); |
| 39 | void EndSystemFrame(); | 39 | void EndSystemFrame(); |
| @@ -87,7 +87,7 @@ private: | |||
| 87 | 87 | ||
| 88 | class SpeedLimiter { | 88 | class SpeedLimiter { |
| 89 | public: | 89 | public: |
| 90 | using Clock = std::chrono::high_resolution_clock; | 90 | using Clock = std::chrono::steady_clock; |
| 91 | 91 | ||
| 92 | void DoSpeedLimiting(std::chrono::microseconds current_system_time_us); | 92 | void DoSpeedLimiting(std::chrono::microseconds current_system_time_us); |
| 93 | 93 | ||
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index dd13d948f..d4fa69a77 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -1,36 +1,32 @@ | |||
| 1 | add_library(input_common STATIC | 1 | add_library(input_common STATIC |
| 2 | analog_from_button.cpp | 2 | drivers/gc_adapter.cpp |
| 3 | analog_from_button.h | 3 | drivers/gc_adapter.h |
| 4 | keyboard.cpp | 4 | drivers/keyboard.cpp |
| 5 | keyboard.h | 5 | drivers/keyboard.h |
| 6 | drivers/mouse.cpp | ||
| 7 | drivers/mouse.h | ||
| 8 | drivers/sdl_driver.cpp | ||
| 9 | drivers/sdl_driver.h | ||
| 10 | drivers/tas_input.cpp | ||
| 11 | drivers/tas_input.h | ||
| 12 | drivers/touch_screen.cpp | ||
| 13 | drivers/touch_screen.h | ||
| 14 | drivers/udp_client.cpp | ||
| 15 | drivers/udp_client.h | ||
| 16 | helpers/stick_from_buttons.cpp | ||
| 17 | helpers/stick_from_buttons.h | ||
| 18 | helpers/touch_from_buttons.cpp | ||
| 19 | helpers/touch_from_buttons.h | ||
| 20 | helpers/udp_protocol.cpp | ||
| 21 | helpers/udp_protocol.h | ||
| 22 | input_engine.cpp | ||
| 23 | input_engine.h | ||
| 24 | input_mapping.cpp | ||
| 25 | input_mapping.h | ||
| 26 | input_poller.cpp | ||
| 27 | input_poller.h | ||
| 6 | main.cpp | 28 | main.cpp |
| 7 | main.h | 29 | main.h |
| 8 | motion_from_button.cpp | ||
| 9 | motion_from_button.h | ||
| 10 | motion_input.cpp | ||
| 11 | motion_input.h | ||
| 12 | touch_from_button.cpp | ||
| 13 | touch_from_button.h | ||
| 14 | gcadapter/gc_adapter.cpp | ||
| 15 | gcadapter/gc_adapter.h | ||
| 16 | gcadapter/gc_poller.cpp | ||
| 17 | gcadapter/gc_poller.h | ||
| 18 | mouse/mouse_input.cpp | ||
| 19 | mouse/mouse_input.h | ||
| 20 | mouse/mouse_poller.cpp | ||
| 21 | mouse/mouse_poller.h | ||
| 22 | sdl/sdl.cpp | ||
| 23 | sdl/sdl.h | ||
| 24 | tas/tas_input.cpp | ||
| 25 | tas/tas_input.h | ||
| 26 | tas/tas_poller.cpp | ||
| 27 | tas/tas_poller.h | ||
| 28 | udp/client.cpp | ||
| 29 | udp/client.h | ||
| 30 | udp/protocol.cpp | ||
| 31 | udp/protocol.h | ||
| 32 | udp/udp.cpp | ||
| 33 | udp/udp.h | ||
| 34 | ) | 30 | ) |
| 35 | 31 | ||
| 36 | if (MSVC) | 32 | if (MSVC) |
| @@ -57,8 +53,8 @@ endif() | |||
| 57 | 53 | ||
| 58 | if (ENABLE_SDL2) | 54 | if (ENABLE_SDL2) |
| 59 | target_sources(input_common PRIVATE | 55 | target_sources(input_common PRIVATE |
| 60 | sdl/sdl_impl.cpp | 56 | drivers/sdl_driver.cpp |
| 61 | sdl/sdl_impl.h | 57 | drivers/sdl_driver.h |
| 62 | ) | 58 | ) |
| 63 | target_link_libraries(input_common PRIVATE SDL2) | 59 | target_link_libraries(input_common PRIVATE SDL2) |
| 64 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) | 60 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) |
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp deleted file mode 100755 index 2fafd077f..000000000 --- a/src/input_common/analog_from_button.cpp +++ /dev/null | |||
| @@ -1,238 +0,0 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <atomic> | ||
| 6 | #include <chrono> | ||
| 7 | #include <cmath> | ||
| 8 | #include <thread> | ||
| 9 | #include "common/math_util.h" | ||
| 10 | #include "common/settings.h" | ||
| 11 | #include "input_common/analog_from_button.h" | ||
| 12 | |||
| 13 | namespace InputCommon { | ||
| 14 | |||
| 15 | class Analog final : public Input::AnalogDevice { | ||
| 16 | public: | ||
| 17 | using Button = std::unique_ptr<Input::ButtonDevice>; | ||
| 18 | |||
| 19 | Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_, | ||
| 20 | float modifier_scale_, float modifier_angle_) | ||
| 21 | : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), | ||
| 22 | right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), | ||
| 23 | modifier_angle(modifier_angle_) { | ||
| 24 | Input::InputCallback<bool> callbacks{ | ||
| 25 | [this]([[maybe_unused]] bool status) { UpdateStatus(); }}; | ||
| 26 | up->SetCallback(callbacks); | ||
| 27 | down->SetCallback(callbacks); | ||
| 28 | left->SetCallback(callbacks); | ||
| 29 | right->SetCallback(callbacks); | ||
| 30 | modifier->SetCallback(callbacks); | ||
| 31 | } | ||
| 32 | |||
| 33 | bool IsAngleGreater(float old_angle, float new_angle) const { | ||
| 34 | constexpr float TAU = Common::PI * 2.0f; | ||
| 35 | // Use wider angle to ease the transition. | ||
| 36 | constexpr float aperture = TAU * 0.15f; | ||
| 37 | const float top_limit = new_angle + aperture; | ||
| 38 | return (old_angle > new_angle && old_angle <= top_limit) || | ||
| 39 | (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); | ||
| 40 | } | ||
| 41 | |||
| 42 | bool IsAngleSmaller(float old_angle, float new_angle) const { | ||
| 43 | constexpr float TAU = Common::PI * 2.0f; | ||
| 44 | // Use wider angle to ease the transition. | ||
| 45 | constexpr float aperture = TAU * 0.15f; | ||
| 46 | const float bottom_limit = new_angle - aperture; | ||
| 47 | return (old_angle >= bottom_limit && old_angle < new_angle) || | ||
| 48 | (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); | ||
| 49 | } | ||
| 50 | |||
| 51 | float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { | ||
| 52 | constexpr float TAU = Common::PI * 2.0f; | ||
| 53 | float new_angle = angle; | ||
| 54 | |||
| 55 | auto time_difference = static_cast<float>( | ||
| 56 | std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); | ||
| 57 | time_difference /= 1000.0f * 1000.0f; | ||
| 58 | if (time_difference > 0.5f) { | ||
| 59 | time_difference = 0.5f; | ||
| 60 | } | ||
| 61 | |||
| 62 | if (IsAngleGreater(new_angle, goal_angle)) { | ||
| 63 | new_angle -= modifier_angle * time_difference; | ||
| 64 | if (new_angle < 0) { | ||
| 65 | new_angle += TAU; | ||
| 66 | } | ||
| 67 | if (!IsAngleGreater(new_angle, goal_angle)) { | ||
| 68 | return goal_angle; | ||
| 69 | } | ||
| 70 | } else if (IsAngleSmaller(new_angle, goal_angle)) { | ||
| 71 | new_angle += modifier_angle * time_difference; | ||
| 72 | if (new_angle >= TAU) { | ||
| 73 | new_angle -= TAU; | ||
| 74 | } | ||
| 75 | if (!IsAngleSmaller(new_angle, goal_angle)) { | ||
| 76 | return goal_angle; | ||
| 77 | } | ||
| 78 | } else { | ||
| 79 | return goal_angle; | ||
| 80 | } | ||
| 81 | return new_angle; | ||
| 82 | } | ||
| 83 | |||
| 84 | void SetGoalAngle(bool r, bool l, bool u, bool d) { | ||
| 85 | // Move to the right | ||
| 86 | if (r && !u && !d) { | ||
| 87 | goal_angle = 0.0f; | ||
| 88 | } | ||
| 89 | |||
| 90 | // Move to the upper right | ||
| 91 | if (r && u && !d) { | ||
| 92 | goal_angle = Common::PI * 0.25f; | ||
| 93 | } | ||
| 94 | |||
| 95 | // Move up | ||
| 96 | if (u && !l && !r) { | ||
| 97 | goal_angle = Common::PI * 0.5f; | ||
| 98 | } | ||
| 99 | |||
| 100 | // Move to the upper left | ||
| 101 | if (l && u && !d) { | ||
| 102 | goal_angle = Common::PI * 0.75f; | ||
| 103 | } | ||
| 104 | |||
| 105 | // Move to the left | ||
| 106 | if (l && !u && !d) { | ||
| 107 | goal_angle = Common::PI; | ||
| 108 | } | ||
| 109 | |||
| 110 | // Move to the bottom left | ||
| 111 | if (l && !u && d) { | ||
| 112 | goal_angle = Common::PI * 1.25f; | ||
| 113 | } | ||
| 114 | |||
| 115 | // Move down | ||
| 116 | if (d && !l && !r) { | ||
| 117 | goal_angle = Common::PI * 1.5f; | ||
| 118 | } | ||
| 119 | |||
| 120 | // Move to the bottom right | ||
| 121 | if (r && !u && d) { | ||
| 122 | goal_angle = Common::PI * 1.75f; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | void UpdateStatus() { | ||
| 127 | const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; | ||
| 128 | |||
| 129 | bool r = right->GetStatus(); | ||
| 130 | bool l = left->GetStatus(); | ||
| 131 | bool u = up->GetStatus(); | ||
| 132 | bool d = down->GetStatus(); | ||
| 133 | |||
| 134 | // Eliminate contradictory movements | ||
| 135 | if (r && l) { | ||
| 136 | r = false; | ||
| 137 | l = false; | ||
| 138 | } | ||
| 139 | if (u && d) { | ||
| 140 | u = false; | ||
| 141 | d = false; | ||
| 142 | } | ||
| 143 | |||
| 144 | // Move if a key is pressed | ||
| 145 | if (r || l || u || d) { | ||
| 146 | amplitude = coef; | ||
| 147 | } else { | ||
| 148 | amplitude = 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | const auto now = std::chrono::steady_clock::now(); | ||
| 152 | const auto time_difference = static_cast<u64>( | ||
| 153 | std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count()); | ||
| 154 | |||
| 155 | if (time_difference < 10) { | ||
| 156 | // Disable analog mode if inputs are too fast | ||
| 157 | SetGoalAngle(r, l, u, d); | ||
| 158 | angle = goal_angle; | ||
| 159 | } else { | ||
| 160 | angle = GetAngle(now); | ||
| 161 | SetGoalAngle(r, l, u, d); | ||
| 162 | } | ||
| 163 | |||
| 164 | last_update = now; | ||
| 165 | } | ||
| 166 | |||
| 167 | std::tuple<float, float> GetStatus() const override { | ||
| 168 | if (Settings::values.emulate_analog_keyboard) { | ||
| 169 | const auto now = std::chrono::steady_clock::now(); | ||
| 170 | float angle_ = GetAngle(now); | ||
| 171 | return std::make_tuple(std::cos(angle_) * amplitude, std::sin(angle_) * amplitude); | ||
| 172 | } | ||
| 173 | constexpr float SQRT_HALF = 0.707106781f; | ||
| 174 | int x = 0, y = 0; | ||
| 175 | if (right->GetStatus()) { | ||
| 176 | ++x; | ||
| 177 | } | ||
| 178 | if (left->GetStatus()) { | ||
| 179 | --x; | ||
| 180 | } | ||
| 181 | if (up->GetStatus()) { | ||
| 182 | ++y; | ||
| 183 | } | ||
| 184 | if (down->GetStatus()) { | ||
| 185 | --y; | ||
| 186 | } | ||
| 187 | const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; | ||
| 188 | return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF), | ||
| 189 | static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF)); | ||
| 190 | } | ||
| 191 | |||
| 192 | Input::AnalogProperties GetAnalogProperties() const override { | ||
| 193 | return {modifier_scale, 1.0f, 0.5f}; | ||
| 194 | } | ||
| 195 | |||
| 196 | bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { | ||
| 197 | switch (direction) { | ||
| 198 | case Input::AnalogDirection::RIGHT: | ||
| 199 | return right->GetStatus(); | ||
| 200 | case Input::AnalogDirection::LEFT: | ||
| 201 | return left->GetStatus(); | ||
| 202 | case Input::AnalogDirection::UP: | ||
| 203 | return up->GetStatus(); | ||
| 204 | case Input::AnalogDirection::DOWN: | ||
| 205 | return down->GetStatus(); | ||
| 206 | } | ||
| 207 | return false; | ||
| 208 | } | ||
| 209 | |||
| 210 | private: | ||
| 211 | Button up; | ||
| 212 | Button down; | ||
| 213 | Button left; | ||
| 214 | Button right; | ||
| 215 | Button modifier; | ||
| 216 | float modifier_scale; | ||
| 217 | float modifier_angle; | ||
| 218 | float angle{}; | ||
| 219 | float goal_angle{}; | ||
| 220 | float amplitude{}; | ||
| 221 | std::chrono::time_point<std::chrono::steady_clock> last_update; | ||
| 222 | }; | ||
| 223 | |||
| 224 | std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { | ||
| 225 | const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); | ||
| 226 | auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine)); | ||
| 227 | auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine)); | ||
| 228 | auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine)); | ||
| 229 | auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); | ||
| 230 | auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); | ||
| 231 | auto modifier_scale = params.Get("modifier_scale", 0.5f); | ||
| 232 | auto modifier_angle = params.Get("modifier_angle", 5.5f); | ||
| 233 | return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), | ||
| 234 | std::move(right), std::move(modifier), modifier_scale, | ||
| 235 | modifier_angle); | ||
| 236 | } | ||
| 237 | |||
| 238 | } // namespace InputCommon | ||
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index a2f1bb67c..7ab4540a8 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp | |||
| @@ -2,47 +2,103 @@ | |||
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2+ |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | 5 | #include <fmt/format.h> |
| 6 | #include <thread> | ||
| 7 | |||
| 8 | #include <libusb.h> | 6 | #include <libusb.h> |
| 9 | 7 | ||
| 10 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 11 | #include "common/param_package.h" | 9 | #include "common/param_package.h" |
| 12 | #include "common/settings_input.h" | 10 | #include "common/settings_input.h" |
| 13 | #include "input_common/gcadapter/gc_adapter.h" | 11 | #include "common/thread.h" |
| 12 | #include "input_common/drivers/gc_adapter.h" | ||
| 13 | |||
| 14 | namespace InputCommon { | ||
| 15 | |||
| 16 | class LibUSBContext { | ||
| 17 | public: | ||
| 18 | explicit LibUSBContext() { | ||
| 19 | init_result = libusb_init(&ctx); | ||
| 20 | } | ||
| 21 | |||
| 22 | ~LibUSBContext() { | ||
| 23 | libusb_exit(ctx); | ||
| 24 | } | ||
| 25 | |||
| 26 | LibUSBContext& operator=(const LibUSBContext&) = delete; | ||
| 27 | LibUSBContext(const LibUSBContext&) = delete; | ||
| 28 | |||
| 29 | LibUSBContext& operator=(LibUSBContext&&) noexcept = delete; | ||
| 30 | LibUSBContext(LibUSBContext&&) noexcept = delete; | ||
| 31 | |||
| 32 | [[nodiscard]] int InitResult() const noexcept { | ||
| 33 | return init_result; | ||
| 34 | } | ||
| 35 | |||
| 36 | [[nodiscard]] libusb_context* get() noexcept { | ||
| 37 | return ctx; | ||
| 38 | } | ||
| 39 | |||
| 40 | private: | ||
| 41 | libusb_context* ctx; | ||
| 42 | int init_result{}; | ||
| 43 | }; | ||
| 44 | |||
| 45 | class LibUSBDeviceHandle { | ||
| 46 | public: | ||
| 47 | explicit LibUSBDeviceHandle(libusb_context* ctx, uint16_t vid, uint16_t pid) noexcept { | ||
| 48 | handle = libusb_open_device_with_vid_pid(ctx, vid, pid); | ||
| 49 | } | ||
| 50 | |||
| 51 | ~LibUSBDeviceHandle() noexcept { | ||
| 52 | if (handle) { | ||
| 53 | libusb_release_interface(handle, 1); | ||
| 54 | libusb_close(handle); | ||
| 55 | } | ||
| 56 | } | ||
| 14 | 57 | ||
| 15 | namespace GCAdapter { | 58 | LibUSBDeviceHandle& operator=(const LibUSBDeviceHandle&) = delete; |
| 59 | LibUSBDeviceHandle(const LibUSBDeviceHandle&) = delete; | ||
| 16 | 60 | ||
| 17 | Adapter::Adapter() { | 61 | LibUSBDeviceHandle& operator=(LibUSBDeviceHandle&&) noexcept = delete; |
| 18 | if (usb_adapter_handle != nullptr) { | 62 | LibUSBDeviceHandle(LibUSBDeviceHandle&&) noexcept = delete; |
| 63 | |||
| 64 | [[nodiscard]] libusb_device_handle* get() noexcept { | ||
| 65 | return handle; | ||
| 66 | } | ||
| 67 | |||
| 68 | private: | ||
| 69 | libusb_device_handle* handle{}; | ||
| 70 | }; | ||
| 71 | |||
| 72 | GCAdapter::GCAdapter(std::string input_engine_) : InputEngine(std::move(input_engine_)) { | ||
| 73 | if (usb_adapter_handle) { | ||
| 19 | return; | 74 | return; |
| 20 | } | 75 | } |
| 21 | LOG_INFO(Input, "GC Adapter Initialization started"); | 76 | LOG_DEBUG(Input, "Initialization started"); |
| 22 | 77 | ||
| 23 | const int init_res = libusb_init(&libusb_ctx); | 78 | libusb_ctx = std::make_unique<LibUSBContext>(); |
| 79 | const int init_res = libusb_ctx->InitResult(); | ||
| 24 | if (init_res == LIBUSB_SUCCESS) { | 80 | if (init_res == LIBUSB_SUCCESS) { |
| 25 | adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); | 81 | adapter_scan_thread = |
| 82 | std::jthread([this](std::stop_token stop_token) { AdapterScanThread(stop_token); }); | ||
| 26 | } else { | 83 | } else { |
| 27 | LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); | 84 | LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); |
| 28 | } | 85 | } |
| 29 | } | 86 | } |
| 30 | 87 | ||
| 31 | Adapter::~Adapter() { | 88 | GCAdapter::~GCAdapter() { |
| 32 | Reset(); | 89 | Reset(); |
| 33 | } | 90 | } |
| 34 | 91 | ||
| 35 | void Adapter::AdapterInputThread() { | 92 | void GCAdapter::AdapterInputThread(std::stop_token stop_token) { |
| 36 | LOG_DEBUG(Input, "GC Adapter input thread started"); | 93 | LOG_DEBUG(Input, "Input thread started"); |
| 94 | Common::SetCurrentThreadName("yuzu:input:GCAdapter"); | ||
| 37 | s32 payload_size{}; | 95 | s32 payload_size{}; |
| 38 | AdapterPayload adapter_payload{}; | 96 | AdapterPayload adapter_payload{}; |
| 39 | 97 | ||
| 40 | if (adapter_scan_thread.joinable()) { | 98 | adapter_scan_thread = {}; |
| 41 | adapter_scan_thread.join(); | ||
| 42 | } | ||
| 43 | 99 | ||
| 44 | while (adapter_input_thread_running) { | 100 | while (!stop_token.stop_requested()) { |
| 45 | libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), | 101 | libusb_interrupt_transfer(usb_adapter_handle->get(), input_endpoint, adapter_payload.data(), |
| 46 | static_cast<s32>(adapter_payload.size()), &payload_size, 16); | 102 | static_cast<s32>(adapter_payload.size()), &payload_size, 16); |
| 47 | if (IsPayloadCorrect(adapter_payload, payload_size)) { | 103 | if (IsPayloadCorrect(adapter_payload, payload_size)) { |
| 48 | UpdateControllers(adapter_payload); | 104 | UpdateControllers(adapter_payload); |
| @@ -52,19 +108,20 @@ void Adapter::AdapterInputThread() { | |||
| 52 | } | 108 | } |
| 53 | 109 | ||
| 54 | if (restart_scan_thread) { | 110 | if (restart_scan_thread) { |
| 55 | adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); | 111 | adapter_scan_thread = |
| 112 | std::jthread([this](std::stop_token token) { AdapterScanThread(token); }); | ||
| 56 | restart_scan_thread = false; | 113 | restart_scan_thread = false; |
| 57 | } | 114 | } |
| 58 | } | 115 | } |
| 59 | 116 | ||
| 60 | bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) { | 117 | bool GCAdapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) { |
| 61 | if (payload_size != static_cast<s32>(adapter_payload.size()) || | 118 | if (payload_size != static_cast<s32>(adapter_payload.size()) || |
| 62 | adapter_payload[0] != LIBUSB_DT_HID) { | 119 | adapter_payload[0] != LIBUSB_DT_HID) { |
| 63 | LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size, | 120 | LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size, |
| 64 | adapter_payload[0]); | 121 | adapter_payload[0]); |
| 65 | if (input_error_counter++ > 20) { | 122 | if (input_error_counter++ > 20) { |
| 66 | LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?"); | 123 | LOG_ERROR(Input, "Timeout, Is the adapter connected?"); |
| 67 | adapter_input_thread_running = false; | 124 | adapter_input_thread.request_stop(); |
| 68 | restart_scan_thread = true; | 125 | restart_scan_thread = true; |
| 69 | } | 126 | } |
| 70 | return false; | 127 | return false; |
| @@ -74,7 +131,7 @@ bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payloa | |||
| 74 | return true; | 131 | return true; |
| 75 | } | 132 | } |
| 76 | 133 | ||
| 77 | void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) { | 134 | void GCAdapter::UpdateControllers(const AdapterPayload& adapter_payload) { |
| 78 | for (std::size_t port = 0; port < pads.size(); ++port) { | 135 | for (std::size_t port = 0; port < pads.size(); ++port) { |
| 79 | const std::size_t offset = 1 + (9 * port); | 136 | const std::size_t offset = 1 + (9 * port); |
| 80 | const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); | 137 | const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); |
| @@ -84,23 +141,24 @@ void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) { | |||
| 84 | const u8 b2 = adapter_payload[offset + 2]; | 141 | const u8 b2 = adapter_payload[offset + 2]; |
| 85 | UpdateStateButtons(port, b1, b2); | 142 | UpdateStateButtons(port, b1, b2); |
| 86 | UpdateStateAxes(port, adapter_payload); | 143 | UpdateStateAxes(port, adapter_payload); |
| 87 | if (configuring) { | ||
| 88 | UpdateYuzuSettings(port); | ||
| 89 | } | ||
| 90 | } | 144 | } |
| 91 | } | 145 | } |
| 92 | } | 146 | } |
| 93 | 147 | ||
| 94 | void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) { | 148 | void GCAdapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) { |
| 95 | if (pads[port].type == pad_type) { | 149 | if (pads[port].type == pad_type) { |
| 96 | return; | 150 | return; |
| 97 | } | 151 | } |
| 98 | // Device changed reset device and set new type | 152 | // Device changed reset device and set new type |
| 99 | ResetDevice(port); | 153 | pads[port].axis_origin = {}; |
| 154 | pads[port].reset_origin_counter = {}; | ||
| 155 | pads[port].enable_vibration = {}; | ||
| 156 | pads[port].rumble_amplitude = {}; | ||
| 100 | pads[port].type = pad_type; | 157 | pads[port].type = pad_type; |
| 101 | } | 158 | } |
| 102 | 159 | ||
| 103 | void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) { | 160 | void GCAdapter::UpdateStateButtons(std::size_t port, [[maybe_unused]] u8 b1, |
| 161 | [[maybe_unused]] u8 b2) { | ||
| 104 | if (port >= pads.size()) { | 162 | if (port >= pads.size()) { |
| 105 | return; | 163 | return; |
| 106 | } | 164 | } |
| @@ -116,25 +174,21 @@ void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) { | |||
| 116 | PadButton::TriggerR, | 174 | PadButton::TriggerR, |
| 117 | PadButton::TriggerL, | 175 | PadButton::TriggerL, |
| 118 | }; | 176 | }; |
| 119 | pads[port].buttons = 0; | 177 | |
| 120 | for (std::size_t i = 0; i < b1_buttons.size(); ++i) { | 178 | for (std::size_t i = 0; i < b1_buttons.size(); ++i) { |
| 121 | if ((b1 & (1U << i)) != 0) { | 179 | const bool button_status = (b1 & (1U << i)) != 0; |
| 122 | pads[port].buttons = | 180 | const int button = static_cast<int>(b1_buttons[i]); |
| 123 | static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i])); | 181 | SetButton(pads[port].identifier, button, button_status); |
| 124 | pads[port].last_button = b1_buttons[i]; | ||
| 125 | } | ||
| 126 | } | 182 | } |
| 127 | 183 | ||
| 128 | for (std::size_t j = 0; j < b2_buttons.size(); ++j) { | 184 | for (std::size_t j = 0; j < b2_buttons.size(); ++j) { |
| 129 | if ((b2 & (1U << j)) != 0) { | 185 | const bool button_status = (b2 & (1U << j)) != 0; |
| 130 | pads[port].buttons = | 186 | const int button = static_cast<int>(b2_buttons[j]); |
| 131 | static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j])); | 187 | SetButton(pads[port].identifier, button, button_status); |
| 132 | pads[port].last_button = b2_buttons[j]; | ||
| 133 | } | ||
| 134 | } | 188 | } |
| 135 | } | 189 | } |
| 136 | 190 | ||
| 137 | void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) { | 191 | void GCAdapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) { |
| 138 | if (port >= pads.size()) { | 192 | if (port >= pads.size()) { |
| 139 | return; | 193 | return; |
| 140 | } | 194 | } |
| @@ -155,134 +209,63 @@ void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_pa | |||
| 155 | pads[port].axis_origin[index] = axis_value; | 209 | pads[port].axis_origin[index] = axis_value; |
| 156 | pads[port].reset_origin_counter++; | 210 | pads[port].reset_origin_counter++; |
| 157 | } | 211 | } |
| 158 | pads[port].axis_values[index] = | 212 | const f32 axis_status = (axis_value - pads[port].axis_origin[index]) / 100.0f; |
| 159 | static_cast<s16>(axis_value - pads[port].axis_origin[index]); | 213 | SetAxis(pads[port].identifier, static_cast<int>(index), axis_status); |
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | void Adapter::UpdateYuzuSettings(std::size_t port) { | ||
| 164 | if (port >= pads.size()) { | ||
| 165 | return; | ||
| 166 | } | ||
| 167 | |||
| 168 | constexpr u8 axis_threshold = 50; | ||
| 169 | GCPadStatus pad_status = {.port = port}; | ||
| 170 | |||
| 171 | if (pads[port].buttons != 0) { | ||
| 172 | pad_status.button = pads[port].last_button; | ||
| 173 | pad_queue.Push(pad_status); | ||
| 174 | } | ||
| 175 | |||
| 176 | // Accounting for a threshold here to ensure an intentional press | ||
| 177 | for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) { | ||
| 178 | const s16 value = pads[port].axis_values[i]; | ||
| 179 | |||
| 180 | if (value > axis_threshold || value < -axis_threshold) { | ||
| 181 | pad_status.axis = static_cast<PadAxes>(i); | ||
| 182 | pad_status.axis_value = value; | ||
| 183 | pad_status.axis_threshold = axis_threshold; | ||
| 184 | pad_queue.Push(pad_status); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | void Adapter::UpdateVibrations() { | ||
| 190 | // Use 8 states to keep the switching between on/off fast enough for | ||
| 191 | // a human to not notice the difference between switching from on/off | ||
| 192 | // More states = more rumble strengths = slower update time | ||
| 193 | constexpr u8 vibration_states = 8; | ||
| 194 | |||
| 195 | vibration_counter = (vibration_counter + 1) % vibration_states; | ||
| 196 | |||
| 197 | for (GCController& pad : pads) { | ||
| 198 | const bool vibrate = pad.rumble_amplitude > vibration_counter; | ||
| 199 | vibration_changed |= vibrate != pad.enable_vibration; | ||
| 200 | pad.enable_vibration = vibrate; | ||
| 201 | } | ||
| 202 | SendVibrations(); | ||
| 203 | } | ||
| 204 | |||
| 205 | void Adapter::SendVibrations() { | ||
| 206 | if (!rumble_enabled || !vibration_changed) { | ||
| 207 | return; | ||
| 208 | } | ||
| 209 | s32 size{}; | ||
| 210 | constexpr u8 rumble_command = 0x11; | ||
| 211 | const u8 p1 = pads[0].enable_vibration; | ||
| 212 | const u8 p2 = pads[1].enable_vibration; | ||
| 213 | const u8 p3 = pads[2].enable_vibration; | ||
| 214 | const u8 p4 = pads[3].enable_vibration; | ||
| 215 | std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4}; | ||
| 216 | const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(), | ||
| 217 | static_cast<s32>(payload.size()), &size, 16); | ||
| 218 | if (err) { | ||
| 219 | LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err)); | ||
| 220 | if (output_error_counter++ > 5) { | ||
| 221 | LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled"); | ||
| 222 | rumble_enabled = false; | ||
| 223 | } | ||
| 224 | return; | ||
| 225 | } | 214 | } |
| 226 | output_error_counter = 0; | ||
| 227 | vibration_changed = false; | ||
| 228 | } | 215 | } |
| 229 | 216 | ||
| 230 | bool Adapter::RumblePlay(std::size_t port, u8 amplitude) { | 217 | void GCAdapter::AdapterScanThread(std::stop_token stop_token) { |
| 231 | pads[port].rumble_amplitude = amplitude; | 218 | Common::SetCurrentThreadName("yuzu:input:ScanGCAdapter"); |
| 232 | 219 | usb_adapter_handle = nullptr; | |
| 233 | return rumble_enabled; | 220 | pads = {}; |
| 234 | } | 221 | while (!stop_token.stop_requested() && !Setup()) { |
| 235 | 222 | std::this_thread::sleep_for(std::chrono::seconds(2)); | |
| 236 | void Adapter::AdapterScanThread() { | ||
| 237 | adapter_scan_thread_running = true; | ||
| 238 | adapter_input_thread_running = false; | ||
| 239 | if (adapter_input_thread.joinable()) { | ||
| 240 | adapter_input_thread.join(); | ||
| 241 | } | ||
| 242 | ClearLibusbHandle(); | ||
| 243 | ResetDevices(); | ||
| 244 | while (adapter_scan_thread_running && !adapter_input_thread_running) { | ||
| 245 | Setup(); | ||
| 246 | std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
| 247 | } | 223 | } |
| 248 | } | 224 | } |
| 249 | 225 | ||
| 250 | void Adapter::Setup() { | 226 | bool GCAdapter::Setup() { |
| 251 | usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337); | 227 | constexpr u16 nintendo_vid = 0x057e; |
| 252 | 228 | constexpr u16 gc_adapter_pid = 0x0337; | |
| 253 | if (usb_adapter_handle == NULL) { | 229 | usb_adapter_handle = |
| 254 | return; | 230 | std::make_unique<LibUSBDeviceHandle>(libusb_ctx->get(), nintendo_vid, gc_adapter_pid); |
| 231 | if (!usb_adapter_handle->get()) { | ||
| 232 | return false; | ||
| 255 | } | 233 | } |
| 256 | if (!CheckDeviceAccess()) { | 234 | if (!CheckDeviceAccess()) { |
| 257 | ClearLibusbHandle(); | 235 | usb_adapter_handle = nullptr; |
| 258 | return; | 236 | return false; |
| 259 | } | 237 | } |
| 260 | 238 | ||
| 261 | libusb_device* device = libusb_get_device(usb_adapter_handle); | 239 | libusb_device* const device = libusb_get_device(usb_adapter_handle->get()); |
| 262 | 240 | ||
| 263 | LOG_INFO(Input, "GC adapter is now connected"); | 241 | LOG_INFO(Input, "GC adapter is now connected"); |
| 264 | // GC Adapter found and accessible, registering it | 242 | // GC Adapter found and accessible, registering it |
| 265 | if (GetGCEndpoint(device)) { | 243 | if (GetGCEndpoint(device)) { |
| 266 | adapter_scan_thread_running = false; | ||
| 267 | adapter_input_thread_running = true; | ||
| 268 | rumble_enabled = true; | 244 | rumble_enabled = true; |
| 269 | input_error_counter = 0; | 245 | input_error_counter = 0; |
| 270 | output_error_counter = 0; | 246 | output_error_counter = 0; |
| 271 | adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | 247 | ||
| 275 | bool Adapter::CheckDeviceAccess() { | 248 | std::size_t port = 0; |
| 276 | // This fixes payload problems from offbrand GCAdapters | 249 | for (GCController& pad : pads) { |
| 277 | const s32 control_transfer_error = | 250 | pad.identifier = { |
| 278 | libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000); | 251 | .guid = Common::UUID{Common::INVALID_UUID}, |
| 279 | if (control_transfer_error < 0) { | 252 | .port = port++, |
| 280 | LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error); | 253 | .pad = 0, |
| 254 | }; | ||
| 255 | PreSetController(pad.identifier); | ||
| 256 | } | ||
| 257 | |||
| 258 | adapter_input_thread = | ||
| 259 | std::jthread([this](std::stop_token stop_token) { AdapterInputThread(stop_token); }); | ||
| 260 | return true; | ||
| 281 | } | 261 | } |
| 262 | return false; | ||
| 263 | } | ||
| 282 | 264 | ||
| 283 | s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); | 265 | bool GCAdapter::CheckDeviceAccess() { |
| 266 | s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle->get(), 0); | ||
| 284 | if (kernel_driver_error == 1) { | 267 | if (kernel_driver_error == 1) { |
| 285 | kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); | 268 | kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle->get(), 0); |
| 286 | if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { | 269 | if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { |
| 287 | LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", | 270 | LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", |
| 288 | kernel_driver_error); | 271 | kernel_driver_error); |
| @@ -290,23 +273,28 @@ bool Adapter::CheckDeviceAccess() { | |||
| 290 | } | 273 | } |
| 291 | 274 | ||
| 292 | if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { | 275 | if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { |
| 293 | libusb_close(usb_adapter_handle); | ||
| 294 | usb_adapter_handle = nullptr; | 276 | usb_adapter_handle = nullptr; |
| 295 | return false; | 277 | return false; |
| 296 | } | 278 | } |
| 297 | 279 | ||
| 298 | const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0); | 280 | const int interface_claim_error = libusb_claim_interface(usb_adapter_handle->get(), 0); |
| 299 | if (interface_claim_error) { | 281 | if (interface_claim_error) { |
| 300 | LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); | 282 | LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); |
| 301 | libusb_close(usb_adapter_handle); | ||
| 302 | usb_adapter_handle = nullptr; | 283 | usb_adapter_handle = nullptr; |
| 303 | return false; | 284 | return false; |
| 304 | } | 285 | } |
| 305 | 286 | ||
| 287 | // This fixes payload problems from offbrand GCAdapters | ||
| 288 | const s32 control_transfer_error = | ||
| 289 | libusb_control_transfer(usb_adapter_handle->get(), 0x21, 11, 0x0001, 0, nullptr, 0, 1000); | ||
| 290 | if (control_transfer_error < 0) { | ||
| 291 | LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error); | ||
| 292 | } | ||
| 293 | |||
| 306 | return true; | 294 | return true; |
| 307 | } | 295 | } |
| 308 | 296 | ||
| 309 | bool Adapter::GetGCEndpoint(libusb_device* device) { | 297 | bool GCAdapter::GetGCEndpoint(libusb_device* device) { |
| 310 | libusb_config_descriptor* config = nullptr; | 298 | libusb_config_descriptor* config = nullptr; |
| 311 | const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); | 299 | const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); |
| 312 | if (config_descriptor_return != LIBUSB_SUCCESS) { | 300 | if (config_descriptor_return != LIBUSB_SUCCESS) { |
| @@ -332,77 +320,95 @@ bool Adapter::GetGCEndpoint(libusb_device* device) { | |||
| 332 | // This transfer seems to be responsible for clearing the state of the adapter | 320 | // This transfer seems to be responsible for clearing the state of the adapter |
| 333 | // Used to clear the "busy" state of when the device is unexpectedly unplugged | 321 | // Used to clear the "busy" state of when the device is unexpectedly unplugged |
| 334 | unsigned char clear_payload = 0x13; | 322 | unsigned char clear_payload = 0x13; |
| 335 | libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, | 323 | libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, &clear_payload, |
| 336 | sizeof(clear_payload), nullptr, 16); | 324 | sizeof(clear_payload), nullptr, 16); |
| 337 | return true; | 325 | return true; |
| 338 | } | 326 | } |
| 339 | 327 | ||
| 340 | void Adapter::JoinThreads() { | 328 | Common::Input::VibrationError GCAdapter::SetRumble( |
| 341 | restart_scan_thread = false; | 329 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { |
| 342 | adapter_input_thread_running = false; | 330 | const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; |
| 343 | adapter_scan_thread_running = false; | 331 | const auto processed_amplitude = |
| 332 | static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8); | ||
| 344 | 333 | ||
| 345 | if (adapter_scan_thread.joinable()) { | 334 | pads[identifier.port].rumble_amplitude = processed_amplitude; |
| 346 | adapter_scan_thread.join(); | ||
| 347 | } | ||
| 348 | 335 | ||
| 349 | if (adapter_input_thread.joinable()) { | 336 | if (!rumble_enabled) { |
| 350 | adapter_input_thread.join(); | 337 | return Common::Input::VibrationError::Disabled; |
| 351 | } | 338 | } |
| 339 | return Common::Input::VibrationError::None; | ||
| 352 | } | 340 | } |
| 353 | 341 | ||
| 354 | void Adapter::ClearLibusbHandle() { | 342 | void GCAdapter::UpdateVibrations() { |
| 355 | if (usb_adapter_handle) { | 343 | // Use 8 states to keep the switching between on/off fast enough for |
| 356 | libusb_release_interface(usb_adapter_handle, 1); | 344 | // a human to feel different vibration strenght |
| 357 | libusb_close(usb_adapter_handle); | 345 | // More states == more rumble strengths == slower update time |
| 358 | usb_adapter_handle = nullptr; | 346 | constexpr u8 vibration_states = 8; |
| 347 | |||
| 348 | vibration_counter = (vibration_counter + 1) % vibration_states; | ||
| 349 | |||
| 350 | for (GCController& pad : pads) { | ||
| 351 | const bool vibrate = pad.rumble_amplitude > vibration_counter; | ||
| 352 | vibration_changed |= vibrate != pad.enable_vibration; | ||
| 353 | pad.enable_vibration = vibrate; | ||
| 359 | } | 354 | } |
| 355 | SendVibrations(); | ||
| 360 | } | 356 | } |
| 361 | 357 | ||
| 362 | void Adapter::ResetDevices() { | 358 | void GCAdapter::SendVibrations() { |
| 363 | for (std::size_t i = 0; i < pads.size(); ++i) { | 359 | if (!rumble_enabled || !vibration_changed) { |
| 364 | ResetDevice(i); | 360 | return; |
| 361 | } | ||
| 362 | s32 size{}; | ||
| 363 | constexpr u8 rumble_command = 0x11; | ||
| 364 | const u8 p1 = pads[0].enable_vibration; | ||
| 365 | const u8 p2 = pads[1].enable_vibration; | ||
| 366 | const u8 p3 = pads[2].enable_vibration; | ||
| 367 | const u8 p4 = pads[3].enable_vibration; | ||
| 368 | std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4}; | ||
| 369 | const int err = | ||
| 370 | libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, payload.data(), | ||
| 371 | static_cast<s32>(payload.size()), &size, 16); | ||
| 372 | if (err) { | ||
| 373 | LOG_DEBUG(Input, "Libusb write failed: {}", libusb_error_name(err)); | ||
| 374 | if (output_error_counter++ > 5) { | ||
| 375 | LOG_ERROR(Input, "Output timeout, Rumble disabled"); | ||
| 376 | rumble_enabled = false; | ||
| 377 | } | ||
| 378 | return; | ||
| 365 | } | 379 | } |
| 380 | output_error_counter = 0; | ||
| 381 | vibration_changed = false; | ||
| 366 | } | 382 | } |
| 367 | 383 | ||
| 368 | void Adapter::ResetDevice(std::size_t port) { | 384 | bool GCAdapter::DeviceConnected(std::size_t port) const { |
| 369 | pads[port].type = ControllerTypes::None; | 385 | return pads[port].type != ControllerTypes::None; |
| 370 | pads[port].enable_vibration = false; | ||
| 371 | pads[port].rumble_amplitude = 0; | ||
| 372 | pads[port].buttons = 0; | ||
| 373 | pads[port].last_button = PadButton::Undefined; | ||
| 374 | pads[port].axis_values.fill(0); | ||
| 375 | pads[port].reset_origin_counter = 0; | ||
| 376 | } | 386 | } |
| 377 | 387 | ||
| 378 | void Adapter::Reset() { | 388 | void GCAdapter::Reset() { |
| 379 | JoinThreads(); | 389 | adapter_scan_thread = {}; |
| 380 | ClearLibusbHandle(); | 390 | adapter_input_thread = {}; |
| 381 | ResetDevices(); | 391 | usb_adapter_handle = nullptr; |
| 382 | 392 | pads = {}; | |
| 383 | if (libusb_ctx) { | 393 | libusb_ctx = nullptr; |
| 384 | libusb_exit(libusb_ctx); | ||
| 385 | } | ||
| 386 | } | 394 | } |
| 387 | 395 | ||
| 388 | std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { | 396 | std::vector<Common::ParamPackage> GCAdapter::GetInputDevices() const { |
| 389 | std::vector<Common::ParamPackage> devices; | 397 | std::vector<Common::ParamPackage> devices; |
| 390 | for (std::size_t port = 0; port < pads.size(); ++port) { | 398 | for (std::size_t port = 0; port < pads.size(); ++port) { |
| 391 | if (!DeviceConnected(port)) { | 399 | if (!DeviceConnected(port)) { |
| 392 | continue; | 400 | continue; |
| 393 | } | 401 | } |
| 394 | std::string name = fmt::format("Gamecube Controller {}", port + 1); | 402 | Common::ParamPackage identifier{}; |
| 395 | devices.emplace_back(Common::ParamPackage{ | 403 | identifier.Set("engine", GetEngineName()); |
| 396 | {"class", "gcpad"}, | 404 | identifier.Set("display", fmt::format("Gamecube Controller {}", port + 1)); |
| 397 | {"display", std::move(name)}, | 405 | identifier.Set("port", static_cast<int>(port)); |
| 398 | {"port", std::to_string(port)}, | 406 | devices.emplace_back(identifier); |
| 399 | }); | ||
| 400 | } | 407 | } |
| 401 | return devices; | 408 | return devices; |
| 402 | } | 409 | } |
| 403 | 410 | ||
| 404 | InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice( | 411 | ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& params) { |
| 405 | const Common::ParamPackage& params) const { | ||
| 406 | // This list is missing ZL/ZR since those are not considered buttons. | 412 | // This list is missing ZL/ZR since those are not considered buttons. |
| 407 | // We will add those afterwards | 413 | // We will add those afterwards |
| 408 | // This list also excludes any button that can't be really mapped | 414 | // This list also excludes any button that can't be really mapped |
| @@ -425,47 +431,49 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice( | |||
| 425 | return {}; | 431 | return {}; |
| 426 | } | 432 | } |
| 427 | 433 | ||
| 428 | InputCommon::ButtonMapping mapping{}; | 434 | ButtonMapping mapping{}; |
| 429 | for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) { | 435 | for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) { |
| 430 | Common::ParamPackage button_params({{"engine", "gcpad"}}); | 436 | Common::ParamPackage button_params{}; |
| 437 | button_params.Set("engine", GetEngineName()); | ||
| 431 | button_params.Set("port", params.Get("port", 0)); | 438 | button_params.Set("port", params.Get("port", 0)); |
| 432 | button_params.Set("button", static_cast<int>(gcadapter_button)); | 439 | button_params.Set("button", static_cast<int>(gcadapter_button)); |
| 433 | mapping.insert_or_assign(switch_button, std::move(button_params)); | 440 | mapping.insert_or_assign(switch_button, std::move(button_params)); |
| 434 | } | 441 | } |
| 435 | 442 | ||
| 436 | // Add the missing bindings for ZL/ZR | 443 | // Add the missing bindings for ZL/ZR |
| 437 | static constexpr std::array<std::pair<Settings::NativeButton::Values, PadAxes>, 2> | 444 | static constexpr std::array<std::tuple<Settings::NativeButton::Values, PadButton, PadAxes>, 2> |
| 438 | switch_to_gcadapter_axis = { | 445 | switch_to_gcadapter_axis = { |
| 439 | std::pair{Settings::NativeButton::ZL, PadAxes::TriggerLeft}, | 446 | std::tuple{Settings::NativeButton::ZL, PadButton::TriggerL, PadAxes::TriggerLeft}, |
| 440 | {Settings::NativeButton::ZR, PadAxes::TriggerRight}, | 447 | {Settings::NativeButton::ZR, PadButton::TriggerR, PadAxes::TriggerRight}, |
| 441 | }; | 448 | }; |
| 442 | for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { | 449 | for (const auto& [switch_button, gcadapter_buton, gcadapter_axis] : switch_to_gcadapter_axis) { |
| 443 | Common::ParamPackage button_params({{"engine", "gcpad"}}); | 450 | Common::ParamPackage button_params{}; |
| 451 | button_params.Set("engine", GetEngineName()); | ||
| 444 | button_params.Set("port", params.Get("port", 0)); | 452 | button_params.Set("port", params.Get("port", 0)); |
| 445 | button_params.Set("button", static_cast<s32>(PadButton::Stick)); | 453 | button_params.Set("button", static_cast<s32>(gcadapter_buton)); |
| 446 | button_params.Set("axis", static_cast<s32>(gcadapter_axis)); | 454 | button_params.Set("axis", static_cast<s32>(gcadapter_axis)); |
| 447 | button_params.Set("threshold", 0.5f); | 455 | button_params.Set("threshold", 0.5f); |
| 456 | button_params.Set("range", 1.9f); | ||
| 448 | button_params.Set("direction", "+"); | 457 | button_params.Set("direction", "+"); |
| 449 | mapping.insert_or_assign(switch_button, std::move(button_params)); | 458 | mapping.insert_or_assign(switch_button, std::move(button_params)); |
| 450 | } | 459 | } |
| 451 | return mapping; | 460 | return mapping; |
| 452 | } | 461 | } |
| 453 | 462 | ||
| 454 | InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice( | 463 | AnalogMapping GCAdapter::GetAnalogMappingForDevice(const Common::ParamPackage& params) { |
| 455 | const Common::ParamPackage& params) const { | ||
| 456 | if (!params.Has("port")) { | 464 | if (!params.Has("port")) { |
| 457 | return {}; | 465 | return {}; |
| 458 | } | 466 | } |
| 459 | 467 | ||
| 460 | InputCommon::AnalogMapping mapping = {}; | 468 | AnalogMapping mapping = {}; |
| 461 | Common::ParamPackage left_analog_params; | 469 | Common::ParamPackage left_analog_params; |
| 462 | left_analog_params.Set("engine", "gcpad"); | 470 | left_analog_params.Set("engine", GetEngineName()); |
| 463 | left_analog_params.Set("port", params.Get("port", 0)); | 471 | left_analog_params.Set("port", params.Get("port", 0)); |
| 464 | left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX)); | 472 | left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX)); |
| 465 | left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY)); | 473 | left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY)); |
| 466 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); | 474 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); |
| 467 | Common::ParamPackage right_analog_params; | 475 | Common::ParamPackage right_analog_params; |
| 468 | right_analog_params.Set("engine", "gcpad"); | 476 | right_analog_params.Set("engine", GetEngineName()); |
| 469 | right_analog_params.Set("port", params.Get("port", 0)); | 477 | right_analog_params.Set("port", params.Get("port", 0)); |
| 470 | right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX)); | 478 | right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX)); |
| 471 | right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY)); | 479 | right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY)); |
| @@ -473,34 +481,47 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice( | |||
| 473 | return mapping; | 481 | return mapping; |
| 474 | } | 482 | } |
| 475 | 483 | ||
| 476 | bool Adapter::DeviceConnected(std::size_t port) const { | 484 | Common::Input::ButtonNames GCAdapter::GetUIButtonName(const Common::ParamPackage& params) const { |
| 477 | return pads[port].type != ControllerTypes::None; | 485 | PadButton button = static_cast<PadButton>(params.Get("button", 0)); |
| 478 | } | 486 | switch (button) { |
| 479 | 487 | case PadButton::ButtonLeft: | |
| 480 | void Adapter::BeginConfiguration() { | 488 | return Common::Input::ButtonNames::ButtonLeft; |
| 481 | pad_queue.Clear(); | 489 | case PadButton::ButtonRight: |
| 482 | configuring = true; | 490 | return Common::Input::ButtonNames::ButtonRight; |
| 483 | } | 491 | case PadButton::ButtonDown: |
| 484 | 492 | return Common::Input::ButtonNames::ButtonDown; | |
| 485 | void Adapter::EndConfiguration() { | 493 | case PadButton::ButtonUp: |
| 486 | pad_queue.Clear(); | 494 | return Common::Input::ButtonNames::ButtonUp; |
| 487 | configuring = false; | 495 | case PadButton::TriggerZ: |
| 488 | } | 496 | return Common::Input::ButtonNames::TriggerZ; |
| 489 | 497 | case PadButton::TriggerR: | |
| 490 | Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() { | 498 | return Common::Input::ButtonNames::TriggerR; |
| 491 | return pad_queue; | 499 | case PadButton::TriggerL: |
| 492 | } | 500 | return Common::Input::ButtonNames::TriggerL; |
| 493 | 501 | case PadButton::ButtonA: | |
| 494 | const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const { | 502 | return Common::Input::ButtonNames::ButtonA; |
| 495 | return pad_queue; | 503 | case PadButton::ButtonB: |
| 504 | return Common::Input::ButtonNames::ButtonB; | ||
| 505 | case PadButton::ButtonX: | ||
| 506 | return Common::Input::ButtonNames::ButtonX; | ||
| 507 | case PadButton::ButtonY: | ||
| 508 | return Common::Input::ButtonNames::ButtonY; | ||
| 509 | case PadButton::ButtonStart: | ||
| 510 | return Common::Input::ButtonNames::ButtonStart; | ||
| 511 | default: | ||
| 512 | return Common::Input::ButtonNames::Undefined; | ||
| 513 | } | ||
| 496 | } | 514 | } |
| 497 | 515 | ||
| 498 | GCController& Adapter::GetPadState(std::size_t port) { | 516 | Common::Input::ButtonNames GCAdapter::GetUIName(const Common::ParamPackage& params) const { |
| 499 | return pads.at(port); | 517 | if (params.Has("button")) { |
| 500 | } | 518 | return GetUIButtonName(params); |
| 519 | } | ||
| 520 | if (params.Has("axis")) { | ||
| 521 | return Common::Input::ButtonNames::Value; | ||
| 522 | } | ||
| 501 | 523 | ||
| 502 | const GCController& Adapter::GetPadState(std::size_t port) const { | 524 | return Common::Input::ButtonNames::Invalid; |
| 503 | return pads.at(port); | ||
| 504 | } | 525 | } |
| 505 | 526 | ||
| 506 | } // namespace GCAdapter | 527 | } // namespace InputCommon |
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h new file mode 100644 index 000000000..7ce1912a3 --- /dev/null +++ b/src/input_common/drivers/gc_adapter.h | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | // Copyright 2014 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <memory> | ||
| 9 | #include <mutex> | ||
| 10 | #include <stop_token> | ||
| 11 | #include <string> | ||
| 12 | #include <thread> | ||
| 13 | |||
| 14 | #include "input_common/input_engine.h" | ||
| 15 | |||
| 16 | struct libusb_context; | ||
| 17 | struct libusb_device; | ||
| 18 | struct libusb_device_handle; | ||
| 19 | |||
| 20 | namespace InputCommon { | ||
| 21 | |||
| 22 | class LibUSBContext; | ||
| 23 | class LibUSBDeviceHandle; | ||
| 24 | |||
| 25 | class GCAdapter : public InputEngine { | ||
| 26 | public: | ||
| 27 | explicit GCAdapter(std::string input_engine_); | ||
| 28 | ~GCAdapter() override; | ||
| 29 | |||
| 30 | Common::Input::VibrationError SetRumble( | ||
| 31 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | ||
| 32 | |||
| 33 | /// Used for automapping features | ||
| 34 | std::vector<Common::ParamPackage> GetInputDevices() const override; | ||
| 35 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; | ||
| 36 | AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; | ||
| 37 | Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; | ||
| 38 | |||
| 39 | private: | ||
| 40 | enum class PadButton { | ||
| 41 | Undefined = 0x0000, | ||
| 42 | ButtonLeft = 0x0001, | ||
| 43 | ButtonRight = 0x0002, | ||
| 44 | ButtonDown = 0x0004, | ||
| 45 | ButtonUp = 0x0008, | ||
| 46 | TriggerZ = 0x0010, | ||
| 47 | TriggerR = 0x0020, | ||
| 48 | TriggerL = 0x0040, | ||
| 49 | ButtonA = 0x0100, | ||
| 50 | ButtonB = 0x0200, | ||
| 51 | ButtonX = 0x0400, | ||
| 52 | ButtonY = 0x0800, | ||
| 53 | ButtonStart = 0x1000, | ||
| 54 | }; | ||
| 55 | |||
| 56 | enum class PadAxes : u8 { | ||
| 57 | StickX, | ||
| 58 | StickY, | ||
| 59 | SubstickX, | ||
| 60 | SubstickY, | ||
| 61 | TriggerLeft, | ||
| 62 | TriggerRight, | ||
| 63 | Undefined, | ||
| 64 | }; | ||
| 65 | |||
| 66 | enum class ControllerTypes { | ||
| 67 | None, | ||
| 68 | Wired, | ||
| 69 | Wireless, | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct GCController { | ||
| 73 | ControllerTypes type = ControllerTypes::None; | ||
| 74 | PadIdentifier identifier{}; | ||
| 75 | bool enable_vibration = false; | ||
| 76 | u8 rumble_amplitude{}; | ||
| 77 | std::array<u8, 6> axis_origin{}; | ||
| 78 | u8 reset_origin_counter{}; | ||
| 79 | }; | ||
| 80 | |||
| 81 | using AdapterPayload = std::array<u8, 37>; | ||
| 82 | |||
| 83 | void UpdatePadType(std::size_t port, ControllerTypes pad_type); | ||
| 84 | void UpdateControllers(const AdapterPayload& adapter_payload); | ||
| 85 | void UpdateStateButtons(std::size_t port, u8 b1, u8 b2); | ||
| 86 | void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload); | ||
| 87 | |||
| 88 | void AdapterInputThread(std::stop_token stop_token); | ||
| 89 | |||
| 90 | void AdapterScanThread(std::stop_token stop_token); | ||
| 91 | |||
| 92 | bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size); | ||
| 93 | |||
| 94 | /// For use in initialization, querying devices to find the adapter | ||
| 95 | bool Setup(); | ||
| 96 | |||
| 97 | /// Returns true if we successfully gain access to GC Adapter | ||
| 98 | bool CheckDeviceAccess(); | ||
| 99 | |||
| 100 | /// Captures GC Adapter endpoint address | ||
| 101 | /// Returns true if the endpoint was set correctly | ||
| 102 | bool GetGCEndpoint(libusb_device* device); | ||
| 103 | |||
| 104 | /// Returns true if there is a device connected to port | ||
| 105 | bool DeviceConnected(std::size_t port) const; | ||
| 106 | |||
| 107 | /// For shutting down, clear all data, join all threads, release usb | ||
| 108 | void Reset(); | ||
| 109 | |||
| 110 | void UpdateVibrations(); | ||
| 111 | |||
| 112 | /// Updates vibration state of all controllers | ||
| 113 | void SendVibrations(); | ||
| 114 | |||
| 115 | Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; | ||
| 116 | |||
| 117 | std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle; | ||
| 118 | std::array<GCController, 4> pads; | ||
| 119 | |||
| 120 | std::jthread adapter_input_thread; | ||
| 121 | std::jthread adapter_scan_thread; | ||
| 122 | bool restart_scan_thread{}; | ||
| 123 | |||
| 124 | std::unique_ptr<LibUSBContext> libusb_ctx; | ||
| 125 | |||
| 126 | u8 input_endpoint{0}; | ||
| 127 | u8 output_endpoint{0}; | ||
| 128 | u8 input_error_counter{0}; | ||
| 129 | u8 output_error_counter{0}; | ||
| 130 | int vibration_counter{0}; | ||
| 131 | |||
| 132 | bool rumble_enabled{true}; | ||
| 133 | bool vibration_changed{true}; | ||
| 134 | }; | ||
| 135 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/keyboard.cpp b/src/input_common/drivers/keyboard.cpp new file mode 100644 index 000000000..4c1e5bbec --- /dev/null +++ b/src/input_common/drivers/keyboard.cpp | |||
| @@ -0,0 +1,112 @@ | |||
| 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/param_package.h" | ||
| 6 | #include "common/settings_input.h" | ||
| 7 | #include "input_common/drivers/keyboard.h" | ||
| 8 | |||
| 9 | namespace InputCommon { | ||
| 10 | |||
| 11 | constexpr PadIdentifier key_identifier = { | ||
| 12 | .guid = Common::UUID{Common::INVALID_UUID}, | ||
| 13 | .port = 0, | ||
| 14 | .pad = 0, | ||
| 15 | }; | ||
| 16 | constexpr PadIdentifier keyboard_key_identifier = { | ||
| 17 | .guid = Common::UUID{Common::INVALID_UUID}, | ||
| 18 | .port = 1, | ||
| 19 | .pad = 0, | ||
| 20 | }; | ||
| 21 | constexpr PadIdentifier keyboard_modifier_identifier = { | ||
| 22 | .guid = Common::UUID{Common::INVALID_UUID}, | ||
| 23 | .port = 1, | ||
| 24 | .pad = 1, | ||
| 25 | }; | ||
| 26 | |||
| 27 | Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) { | ||
| 28 | // Keyboard is broken into 3 diferent sets: | ||
| 29 | // key: Unfiltered intended for controllers. | ||
| 30 | // keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation. | ||
| 31 | // keyboard_modifier: Allows only Settings::NativeKeyboard::Modifiers intended for keyboard | ||
| 32 | // emulation. | ||
| 33 | PreSetController(key_identifier); | ||
| 34 | PreSetController(keyboard_key_identifier); | ||
| 35 | PreSetController(keyboard_modifier_identifier); | ||
| 36 | } | ||
| 37 | |||
| 38 | void Keyboard::PressKey(int key_code) { | ||
| 39 | SetButton(key_identifier, key_code, true); | ||
| 40 | } | ||
| 41 | |||
| 42 | void Keyboard::ReleaseKey(int key_code) { | ||
| 43 | SetButton(key_identifier, key_code, false); | ||
| 44 | } | ||
| 45 | |||
| 46 | void Keyboard::PressKeyboardKey(int key_index) { | ||
| 47 | if (key_index == Settings::NativeKeyboard::None) { | ||
| 48 | return; | ||
| 49 | } | ||
| 50 | SetButton(keyboard_key_identifier, key_index, true); | ||
| 51 | } | ||
| 52 | |||
| 53 | void Keyboard::ReleaseKeyboardKey(int key_index) { | ||
| 54 | if (key_index == Settings::NativeKeyboard::None) { | ||
| 55 | return; | ||
| 56 | } | ||
| 57 | SetButton(keyboard_key_identifier, key_index, false); | ||
| 58 | } | ||
| 59 | |||
| 60 | void Keyboard::SetKeyboardModifiers(int key_modifiers) { | ||
| 61 | for (int i = 0; i < 32; ++i) { | ||
| 62 | bool key_value = ((key_modifiers >> i) & 0x1) != 0; | ||
| 63 | SetButton(keyboard_modifier_identifier, i, key_value); | ||
| 64 | // Use the modifier to press the key button equivalent | ||
| 65 | switch (i) { | ||
| 66 | case Settings::NativeKeyboard::LeftControl: | ||
| 67 | SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftControlKey, key_value); | ||
| 68 | break; | ||
| 69 | case Settings::NativeKeyboard::LeftShift: | ||
| 70 | SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftShiftKey, key_value); | ||
| 71 | break; | ||
| 72 | case Settings::NativeKeyboard::LeftAlt: | ||
| 73 | SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftAltKey, key_value); | ||
| 74 | break; | ||
| 75 | case Settings::NativeKeyboard::LeftMeta: | ||
| 76 | SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftMetaKey, key_value); | ||
| 77 | break; | ||
| 78 | case Settings::NativeKeyboard::RightControl: | ||
| 79 | SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightControlKey, | ||
| 80 | key_value); | ||
| 81 | break; | ||
| 82 | case Settings::NativeKeyboard::RightShift: | ||
| 83 | SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightShiftKey, key_value); | ||
| 84 | break; | ||
| 85 | case Settings::NativeKeyboard::RightAlt: | ||
| 86 | SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightAltKey, key_value); | ||
| 87 | break; | ||
| 88 | case Settings::NativeKeyboard::RightMeta: | ||
| 89 | SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightMetaKey, key_value); | ||
| 90 | break; | ||
| 91 | default: | ||
| 92 | // Other modifier keys should be pressed with PressKey since they stay enabled until | ||
| 93 | // next press | ||
| 94 | break; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | void Keyboard::ReleaseAllKeys() { | ||
| 100 | ResetButtonState(); | ||
| 101 | } | ||
| 102 | |||
| 103 | std::vector<Common::ParamPackage> Keyboard::GetInputDevices() const { | ||
| 104 | std::vector<Common::ParamPackage> devices; | ||
| 105 | devices.emplace_back(Common::ParamPackage{ | ||
| 106 | {"engine", GetEngineName()}, | ||
| 107 | {"display", "Keyboard Only"}, | ||
| 108 | }); | ||
| 109 | return devices; | ||
| 110 | } | ||
| 111 | |||
| 112 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/keyboard.h b/src/input_common/drivers/keyboard.h new file mode 100644 index 000000000..3856c882c --- /dev/null +++ b/src/input_common/drivers/keyboard.h | |||
| @@ -0,0 +1,56 @@ | |||
| 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 "input_common/input_engine.h" | ||
| 8 | |||
| 9 | namespace InputCommon { | ||
| 10 | |||
| 11 | /** | ||
| 12 | * A button device factory representing a keyboard. It receives keyboard events and forward them | ||
| 13 | * to all button devices it created. | ||
| 14 | */ | ||
| 15 | class Keyboard final : public InputEngine { | ||
| 16 | public: | ||
| 17 | explicit Keyboard(std::string input_engine_); | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Sets the status of all buttons bound with the key to pressed | ||
| 21 | * @param key_code the code of the key to press | ||
| 22 | */ | ||
| 23 | void PressKey(int key_code); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Sets the status of all buttons bound with the key to released | ||
| 27 | * @param key_code the code of the key to release | ||
| 28 | */ | ||
| 29 | void ReleaseKey(int key_code); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Sets the status of the keyboard key to pressed | ||
| 33 | * @param key_index index of the key to press | ||
| 34 | */ | ||
| 35 | void PressKeyboardKey(int key_index); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Sets the status of the keyboard key to released | ||
| 39 | * @param key_index index of the key to release | ||
| 40 | */ | ||
| 41 | void ReleaseKeyboardKey(int key_index); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Sets the status of all keyboard modifier keys | ||
| 45 | * @param key_modifiers the code of the key to release | ||
| 46 | */ | ||
| 47 | void SetKeyboardModifiers(int key_modifiers); | ||
| 48 | |||
| 49 | /// Sets all keys to the non pressed state | ||
| 50 | void ReleaseAllKeys(); | ||
| 51 | |||
| 52 | /// Used for automapping features | ||
| 53 | std::vector<Common::ParamPackage> GetInputDevices() const override; | ||
| 54 | }; | ||
| 55 | |||
| 56 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp new file mode 100644 index 000000000..aa69216c8 --- /dev/null +++ b/src/input_common/drivers/mouse.cpp | |||
| @@ -0,0 +1,185 @@ | |||
| 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 <stop_token> | ||
| 6 | #include <thread> | ||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | #include "common/param_package.h" | ||
| 10 | #include "common/settings.h" | ||
| 11 | #include "common/thread.h" | ||
| 12 | #include "input_common/drivers/mouse.h" | ||
| 13 | |||
| 14 | namespace InputCommon { | ||
| 15 | constexpr int mouse_axis_x = 0; | ||
| 16 | constexpr int mouse_axis_y = 1; | ||
| 17 | constexpr int wheel_axis_x = 2; | ||
| 18 | constexpr int wheel_axis_y = 3; | ||
| 19 | constexpr int touch_axis_x = 10; | ||
| 20 | constexpr int touch_axis_y = 11; | ||
| 21 | constexpr PadIdentifier identifier = { | ||
| 22 | .guid = Common::UUID{Common::INVALID_UUID}, | ||
| 23 | .port = 0, | ||
| 24 | .pad = 0, | ||
| 25 | }; | ||
| 26 | |||
| 27 | Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) { | ||
| 28 | PreSetController(identifier); | ||
| 29 | PreSetAxis(identifier, mouse_axis_x); | ||
| 30 | PreSetAxis(identifier, mouse_axis_y); | ||
| 31 | PreSetAxis(identifier, wheel_axis_x); | ||
| 32 | PreSetAxis(identifier, wheel_axis_y); | ||
| 33 | PreSetAxis(identifier, touch_axis_x); | ||
| 34 | PreSetAxis(identifier, touch_axis_x); | ||
| 35 | update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); }); | ||
| 36 | } | ||
| 37 | |||
| 38 | void Mouse::UpdateThread(std::stop_token stop_token) { | ||
| 39 | Common::SetCurrentThreadName("yuzu:input:Mouse"); | ||
| 40 | constexpr int update_time = 10; | ||
| 41 | while (!stop_token.stop_requested()) { | ||
| 42 | if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { | ||
| 43 | // Slow movement by 4% | ||
| 44 | last_mouse_change *= 0.96f; | ||
| 45 | const float sensitivity = | ||
| 46 | Settings::values.mouse_panning_sensitivity.GetValue() * 0.022f; | ||
| 47 | SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity); | ||
| 48 | SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity); | ||
| 49 | } | ||
| 50 | |||
| 51 | if (mouse_panning_timout++ > 20) { | ||
| 52 | StopPanning(); | ||
| 53 | } | ||
| 54 | std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | void Mouse::MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y) { | ||
| 59 | // If native mouse is enabled just set the screen coordinates | ||
| 60 | if (Settings::values.mouse_enabled) { | ||
| 61 | SetAxis(identifier, mouse_axis_x, touch_x); | ||
| 62 | SetAxis(identifier, mouse_axis_y, touch_y); | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | SetAxis(identifier, touch_axis_x, touch_x); | ||
| 67 | SetAxis(identifier, touch_axis_y, touch_y); | ||
| 68 | |||
| 69 | if (Settings::values.mouse_panning) { | ||
| 70 | auto mouse_change = | ||
| 71 | (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); | ||
| 72 | mouse_panning_timout = 0; | ||
| 73 | |||
| 74 | const auto move_distance = mouse_change.Length(); | ||
| 75 | if (move_distance == 0) { | ||
| 76 | return; | ||
| 77 | } | ||
| 78 | |||
| 79 | // Make slow movements at least 3 units on lenght | ||
| 80 | if (move_distance < 3.0f) { | ||
| 81 | // Normalize value | ||
| 82 | mouse_change /= move_distance; | ||
| 83 | mouse_change *= 3.0f; | ||
| 84 | } | ||
| 85 | |||
| 86 | // Average mouse movements | ||
| 87 | last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f); | ||
| 88 | |||
| 89 | const auto last_move_distance = last_mouse_change.Length(); | ||
| 90 | |||
| 91 | // Make fast movements clamp to 8 units on lenght | ||
| 92 | if (last_move_distance > 8.0f) { | ||
| 93 | // Normalize value | ||
| 94 | last_mouse_change /= last_move_distance; | ||
| 95 | last_mouse_change *= 8.0f; | ||
| 96 | } | ||
| 97 | |||
| 98 | // Ignore average if it's less than 1 unit and use current movement value | ||
| 99 | if (last_move_distance < 1.0f) { | ||
| 100 | last_mouse_change = mouse_change / mouse_change.Length(); | ||
| 101 | } | ||
| 102 | |||
| 103 | return; | ||
| 104 | } | ||
| 105 | |||
| 106 | if (button_pressed) { | ||
| 107 | const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin; | ||
| 108 | const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f; | ||
| 109 | SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * sensitivity); | ||
| 110 | SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * sensitivity); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | void Mouse::PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button) { | ||
| 115 | SetAxis(identifier, touch_axis_x, touch_x); | ||
| 116 | SetAxis(identifier, touch_axis_y, touch_y); | ||
| 117 | SetButton(identifier, static_cast<int>(button), true); | ||
| 118 | // Set initial analog parameters | ||
| 119 | mouse_origin = {x, y}; | ||
| 120 | last_mouse_position = {x, y}; | ||
| 121 | button_pressed = true; | ||
| 122 | } | ||
| 123 | |||
| 124 | void Mouse::ReleaseButton(MouseButton button) { | ||
| 125 | SetButton(identifier, static_cast<int>(button), false); | ||
| 126 | |||
| 127 | if (!Settings::values.mouse_panning && !Settings::values.mouse_enabled) { | ||
| 128 | SetAxis(identifier, mouse_axis_x, 0); | ||
| 129 | SetAxis(identifier, mouse_axis_y, 0); | ||
| 130 | } | ||
| 131 | button_pressed = false; | ||
| 132 | } | ||
| 133 | |||
| 134 | void Mouse::MouseWheelChange(int x, int y) { | ||
| 135 | wheel_position.x += x; | ||
| 136 | wheel_position.y += y; | ||
| 137 | SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); | ||
| 138 | SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); | ||
| 139 | } | ||
| 140 | |||
| 141 | void Mouse::ReleaseAllButtons() { | ||
| 142 | ResetButtonState(); | ||
| 143 | button_pressed = false; | ||
| 144 | } | ||
| 145 | |||
| 146 | void Mouse::StopPanning() { | ||
| 147 | last_mouse_change = {}; | ||
| 148 | } | ||
| 149 | |||
| 150 | std::vector<Common::ParamPackage> Mouse::GetInputDevices() const { | ||
| 151 | std::vector<Common::ParamPackage> devices; | ||
| 152 | devices.emplace_back(Common::ParamPackage{ | ||
| 153 | {"engine", GetEngineName()}, | ||
| 154 | {"display", "Keyboard/Mouse"}, | ||
| 155 | }); | ||
| 156 | return devices; | ||
| 157 | } | ||
| 158 | |||
| 159 | AnalogMapping Mouse::GetAnalogMappingForDevice( | ||
| 160 | [[maybe_unused]] const Common::ParamPackage& params) { | ||
| 161 | // Only overwrite different buttons from default | ||
| 162 | AnalogMapping mapping = {}; | ||
| 163 | Common::ParamPackage right_analog_params; | ||
| 164 | right_analog_params.Set("engine", GetEngineName()); | ||
| 165 | right_analog_params.Set("axis_x", 0); | ||
| 166 | right_analog_params.Set("axis_y", 1); | ||
| 167 | right_analog_params.Set("threshold", 0.5f); | ||
| 168 | right_analog_params.Set("range", 1.0f); | ||
| 169 | right_analog_params.Set("deadzone", 0.0f); | ||
| 170 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params)); | ||
| 171 | return mapping; | ||
| 172 | } | ||
| 173 | |||
| 174 | Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) const { | ||
| 175 | if (params.Has("button")) { | ||
| 176 | return Common::Input::ButtonNames::Value; | ||
| 177 | } | ||
| 178 | if (params.Has("axis")) { | ||
| 179 | return Common::Input::ButtonNames::Value; | ||
| 180 | } | ||
| 181 | |||
| 182 | return Common::Input::ButtonNames::Invalid; | ||
| 183 | } | ||
| 184 | |||
| 185 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h new file mode 100644 index 000000000..040446178 --- /dev/null +++ b/src/input_common/drivers/mouse.h | |||
| @@ -0,0 +1,81 @@ | |||
| 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 <stop_token> | ||
| 8 | #include <thread> | ||
| 9 | |||
| 10 | #include "common/vector_math.h" | ||
| 11 | #include "input_common/input_engine.h" | ||
| 12 | |||
| 13 | namespace InputCommon { | ||
| 14 | |||
| 15 | enum class MouseButton { | ||
| 16 | Left, | ||
| 17 | Right, | ||
| 18 | Wheel, | ||
| 19 | Backward, | ||
| 20 | Forward, | ||
| 21 | Task, | ||
| 22 | Extra, | ||
| 23 | Undefined, | ||
| 24 | }; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * A button device factory representing a keyboard. It receives keyboard events and forward them | ||
| 28 | * to all button devices it created. | ||
| 29 | */ | ||
| 30 | class Mouse final : public InputEngine { | ||
| 31 | public: | ||
| 32 | explicit Mouse(std::string input_engine_); | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Signals that mouse has moved. | ||
| 36 | * @param x the x-coordinate of the cursor | ||
| 37 | * @param y the y-coordinate of the cursor | ||
| 38 | * @param center_x the x-coordinate of the middle of the screen | ||
| 39 | * @param center_y the y-coordinate of the middle of the screen | ||
| 40 | */ | ||
| 41 | void MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Sets the status of all buttons bound with the key to pressed | ||
| 45 | * @param key_code the code of the key to press | ||
| 46 | */ | ||
| 47 | void PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Sets the status of all buttons bound with the key to released | ||
| 51 | * @param key_code the code of the key to release | ||
| 52 | */ | ||
| 53 | void ReleaseButton(MouseButton button); | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Sets the status of the mouse wheel | ||
| 57 | * @param x delta movement in the x direction | ||
| 58 | * @param y delta movement in the y direction | ||
| 59 | */ | ||
| 60 | void MouseWheelChange(int x, int y); | ||
| 61 | |||
| 62 | void ReleaseAllButtons(); | ||
| 63 | |||
| 64 | std::vector<Common::ParamPackage> GetInputDevices() const override; | ||
| 65 | AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; | ||
| 66 | Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; | ||
| 67 | |||
| 68 | private: | ||
| 69 | void UpdateThread(std::stop_token stop_token); | ||
| 70 | void StopPanning(); | ||
| 71 | |||
| 72 | Common::Vec2<int> mouse_origin; | ||
| 73 | Common::Vec2<int> last_mouse_position; | ||
| 74 | Common::Vec2<float> last_mouse_change; | ||
| 75 | Common::Vec2<int> wheel_position; | ||
| 76 | bool button_pressed; | ||
| 77 | int mouse_panning_timout{}; | ||
| 78 | std::jthread update_thread; | ||
| 79 | }; | ||
| 80 | |||
| 81 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp new file mode 100644 index 000000000..0cda9df62 --- /dev/null +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -0,0 +1,926 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "common/math_util.h" | ||
| 7 | #include "common/param_package.h" | ||
| 8 | #include "common/settings.h" | ||
| 9 | #include "common/thread.h" | ||
| 10 | #include "common/vector_math.h" | ||
| 11 | #include "input_common/drivers/sdl_driver.h" | ||
| 12 | |||
| 13 | namespace InputCommon { | ||
| 14 | |||
| 15 | namespace { | ||
| 16 | std::string GetGUID(SDL_Joystick* joystick) { | ||
| 17 | const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); | ||
| 18 | char guid_str[33]; | ||
| 19 | SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); | ||
| 20 | return guid_str; | ||
| 21 | } | ||
| 22 | } // Anonymous namespace | ||
| 23 | |||
| 24 | static int SDLEventWatcher(void* user_data, SDL_Event* event) { | ||
| 25 | auto* const sdl_state = static_cast<SDLDriver*>(user_data); | ||
| 26 | |||
| 27 | sdl_state->HandleGameControllerEvent(*event); | ||
| 28 | |||
| 29 | return 0; | ||
| 30 | } | ||
| 31 | |||
| 32 | class SDLJoystick { | ||
| 33 | public: | ||
| 34 | SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, | ||
| 35 | SDL_GameController* game_controller) | ||
| 36 | : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, | ||
| 37 | sdl_controller{game_controller, &SDL_GameControllerClose} { | ||
| 38 | EnableMotion(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void EnableMotion() { | ||
| 42 | if (sdl_controller) { | ||
| 43 | SDL_GameController* controller = sdl_controller.get(); | ||
| 44 | if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) { | ||
| 45 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); | ||
| 46 | has_accel = true; | ||
| 47 | } | ||
| 48 | if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) { | ||
| 49 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); | ||
| 50 | has_gyro = true; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | bool HasGyro() const { | ||
| 56 | return has_gyro; | ||
| 57 | } | ||
| 58 | |||
| 59 | bool HasAccel() const { | ||
| 60 | return has_accel; | ||
| 61 | } | ||
| 62 | |||
| 63 | bool UpdateMotion(SDL_ControllerSensorEvent event) { | ||
| 64 | constexpr float gravity_constant = 9.80665f; | ||
| 65 | std::lock_guard lock{mutex}; | ||
| 66 | const u64 time_difference = event.timestamp - last_motion_update; | ||
| 67 | last_motion_update = event.timestamp; | ||
| 68 | switch (event.sensor) { | ||
| 69 | case SDL_SENSOR_ACCEL: { | ||
| 70 | motion.accel_x = -event.data[0] / gravity_constant; | ||
| 71 | motion.accel_y = event.data[2] / gravity_constant; | ||
| 72 | motion.accel_z = -event.data[1] / gravity_constant; | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | case SDL_SENSOR_GYRO: { | ||
| 76 | motion.gyro_x = event.data[0] / (Common::PI * 2); | ||
| 77 | motion.gyro_y = -event.data[2] / (Common::PI * 2); | ||
| 78 | motion.gyro_z = event.data[1] / (Common::PI * 2); | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | // Ignore duplicated timestamps | ||
| 84 | if (time_difference == 0) { | ||
| 85 | return false; | ||
| 86 | } | ||
| 87 | motion.delta_timestamp = time_difference * 1000; | ||
| 88 | return true; | ||
| 89 | } | ||
| 90 | |||
| 91 | const BasicMotion& GetMotion() const { | ||
| 92 | return motion; | ||
| 93 | } | ||
| 94 | |||
| 95 | bool RumblePlay(const Common::Input::VibrationStatus vibration) { | ||
| 96 | constexpr u32 rumble_max_duration_ms = 1000; | ||
| 97 | if (sdl_controller) { | ||
| 98 | return SDL_GameControllerRumble( | ||
| 99 | sdl_controller.get(), static_cast<u16>(vibration.low_amplitude), | ||
| 100 | static_cast<u16>(vibration.high_amplitude), rumble_max_duration_ms) != -1; | ||
| 101 | } else if (sdl_joystick) { | ||
| 102 | return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(vibration.low_amplitude), | ||
| 103 | static_cast<u16>(vibration.high_amplitude), | ||
| 104 | rumble_max_duration_ms) != -1; | ||
| 105 | } | ||
| 106 | |||
| 107 | return false; | ||
| 108 | } | ||
| 109 | |||
| 110 | bool HasHDRumble() const { | ||
| 111 | if (sdl_controller) { | ||
| 112 | return (SDL_GameControllerGetType(sdl_controller.get()) == | ||
| 113 | SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO); | ||
| 114 | } | ||
| 115 | return false; | ||
| 116 | } | ||
| 117 | /** | ||
| 118 | * The Pad identifier of the joystick | ||
| 119 | */ | ||
| 120 | const PadIdentifier GetPadIdentifier() const { | ||
| 121 | return { | ||
| 122 | .guid = Common::UUID{guid}, | ||
| 123 | .port = static_cast<std::size_t>(port), | ||
| 124 | .pad = 0, | ||
| 125 | }; | ||
| 126 | } | ||
| 127 | |||
| 128 | /** | ||
| 129 | * The guid of the joystick | ||
| 130 | */ | ||
| 131 | const std::string& GetGUID() const { | ||
| 132 | return guid; | ||
| 133 | } | ||
| 134 | |||
| 135 | /** | ||
| 136 | * The number of joystick from the same type that were connected before this joystick | ||
| 137 | */ | ||
| 138 | int GetPort() const { | ||
| 139 | return port; | ||
| 140 | } | ||
| 141 | |||
| 142 | SDL_Joystick* GetSDLJoystick() const { | ||
| 143 | return sdl_joystick.get(); | ||
| 144 | } | ||
| 145 | |||
| 146 | SDL_GameController* GetSDLGameController() const { | ||
| 147 | return sdl_controller.get(); | ||
| 148 | } | ||
| 149 | |||
| 150 | void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) { | ||
| 151 | sdl_joystick.reset(joystick); | ||
| 152 | sdl_controller.reset(controller); | ||
| 153 | } | ||
| 154 | |||
| 155 | bool IsJoyconLeft() const { | ||
| 156 | const std::string controller_name = GetControllerName(); | ||
| 157 | if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) { | ||
| 158 | return true; | ||
| 159 | } | ||
| 160 | if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) { | ||
| 161 | return true; | ||
| 162 | } | ||
| 163 | return false; | ||
| 164 | } | ||
| 165 | |||
| 166 | bool IsJoyconRight() const { | ||
| 167 | const std::string controller_name = GetControllerName(); | ||
| 168 | if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) { | ||
| 169 | return true; | ||
| 170 | } | ||
| 171 | if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) { | ||
| 172 | return true; | ||
| 173 | } | ||
| 174 | return false; | ||
| 175 | } | ||
| 176 | |||
| 177 | BatteryLevel GetBatteryLevel() { | ||
| 178 | const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); | ||
| 179 | switch (level) { | ||
| 180 | case SDL_JOYSTICK_POWER_EMPTY: | ||
| 181 | return BatteryLevel::Empty; | ||
| 182 | case SDL_JOYSTICK_POWER_LOW: | ||
| 183 | return BatteryLevel::Critical; | ||
| 184 | case SDL_JOYSTICK_POWER_MEDIUM: | ||
| 185 | return BatteryLevel::Low; | ||
| 186 | case SDL_JOYSTICK_POWER_FULL: | ||
| 187 | return BatteryLevel::Medium; | ||
| 188 | case SDL_JOYSTICK_POWER_MAX: | ||
| 189 | return BatteryLevel::Full; | ||
| 190 | case SDL_JOYSTICK_POWER_UNKNOWN: | ||
| 191 | case SDL_JOYSTICK_POWER_WIRED: | ||
| 192 | default: | ||
| 193 | return BatteryLevel::Charging; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | std::string GetControllerName() const { | ||
| 198 | if (sdl_controller) { | ||
| 199 | switch (SDL_GameControllerGetType(sdl_controller.get())) { | ||
| 200 | case SDL_CONTROLLER_TYPE_XBOX360: | ||
| 201 | return "XBox 360 Controller"; | ||
| 202 | case SDL_CONTROLLER_TYPE_XBOXONE: | ||
| 203 | return "XBox One Controller"; | ||
| 204 | default: | ||
| 205 | break; | ||
| 206 | } | ||
| 207 | const auto name = SDL_GameControllerName(sdl_controller.get()); | ||
| 208 | if (name) { | ||
| 209 | return name; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | if (sdl_joystick) { | ||
| 214 | const auto name = SDL_JoystickName(sdl_joystick.get()); | ||
| 215 | if (name) { | ||
| 216 | return name; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | return "Unknown"; | ||
| 221 | } | ||
| 222 | |||
| 223 | private: | ||
| 224 | std::string guid; | ||
| 225 | int port; | ||
| 226 | std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; | ||
| 227 | std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; | ||
| 228 | mutable std::mutex mutex; | ||
| 229 | |||
| 230 | u64 last_motion_update{}; | ||
| 231 | bool has_gyro{false}; | ||
| 232 | bool has_accel{false}; | ||
| 233 | BasicMotion motion; | ||
| 234 | }; | ||
| 235 | |||
| 236 | std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) { | ||
| 237 | std::lock_guard lock{joystick_map_mutex}; | ||
| 238 | const auto it = joystick_map.find(guid); | ||
| 239 | |||
| 240 | if (it != joystick_map.end()) { | ||
| 241 | while (it->second.size() <= static_cast<std::size_t>(port)) { | ||
| 242 | auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), | ||
| 243 | nullptr, nullptr); | ||
| 244 | it->second.emplace_back(std::move(joystick)); | ||
| 245 | } | ||
| 246 | |||
| 247 | return it->second[static_cast<std::size_t>(port)]; | ||
| 248 | } | ||
| 249 | |||
| 250 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr); | ||
| 251 | |||
| 252 | return joystick_map[guid].emplace_back(std::move(joystick)); | ||
| 253 | } | ||
| 254 | |||
| 255 | std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { | ||
| 256 | auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); | ||
| 257 | const std::string guid = GetGUID(sdl_joystick); | ||
| 258 | |||
| 259 | std::lock_guard lock{joystick_map_mutex}; | ||
| 260 | const auto map_it = joystick_map.find(guid); | ||
| 261 | |||
| 262 | if (map_it == joystick_map.end()) { | ||
| 263 | return nullptr; | ||
| 264 | } | ||
| 265 | |||
| 266 | const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), | ||
| 267 | [&sdl_joystick](const auto& joystick) { | ||
| 268 | return joystick->GetSDLJoystick() == sdl_joystick; | ||
| 269 | }); | ||
| 270 | |||
| 271 | if (vec_it == map_it->second.end()) { | ||
| 272 | return nullptr; | ||
| 273 | } | ||
| 274 | |||
| 275 | return *vec_it; | ||
| 276 | } | ||
| 277 | |||
| 278 | void SDLDriver::InitJoystick(int joystick_index) { | ||
| 279 | SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); | ||
| 280 | SDL_GameController* sdl_gamecontroller = nullptr; | ||
| 281 | |||
| 282 | if (SDL_IsGameController(joystick_index)) { | ||
| 283 | sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); | ||
| 284 | } | ||
| 285 | |||
| 286 | if (!sdl_joystick) { | ||
| 287 | LOG_ERROR(Input, "Failed to open joystick {}", joystick_index); | ||
| 288 | return; | ||
| 289 | } | ||
| 290 | |||
| 291 | const std::string guid = GetGUID(sdl_joystick); | ||
| 292 | |||
| 293 | std::lock_guard lock{joystick_map_mutex}; | ||
| 294 | if (joystick_map.find(guid) == joystick_map.end()) { | ||
| 295 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); | ||
| 296 | PreSetController(joystick->GetPadIdentifier()); | ||
| 297 | SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); | ||
| 298 | joystick_map[guid].emplace_back(std::move(joystick)); | ||
| 299 | return; | ||
| 300 | } | ||
| 301 | |||
| 302 | auto& joystick_guid_list = joystick_map[guid]; | ||
| 303 | const auto joystick_it = | ||
| 304 | std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), | ||
| 305 | [](const auto& joystick) { return !joystick->GetSDLJoystick(); }); | ||
| 306 | |||
| 307 | if (joystick_it != joystick_guid_list.end()) { | ||
| 308 | (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); | ||
| 309 | return; | ||
| 310 | } | ||
| 311 | |||
| 312 | const int port = static_cast<int>(joystick_guid_list.size()); | ||
| 313 | auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); | ||
| 314 | PreSetController(joystick->GetPadIdentifier()); | ||
| 315 | SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); | ||
| 316 | joystick_guid_list.emplace_back(std::move(joystick)); | ||
| 317 | } | ||
| 318 | |||
| 319 | void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) { | ||
| 320 | const std::string guid = GetGUID(sdl_joystick); | ||
| 321 | |||
| 322 | std::lock_guard lock{joystick_map_mutex}; | ||
| 323 | // This call to guid is safe since the joystick is guaranteed to be in the map | ||
| 324 | const auto& joystick_guid_list = joystick_map[guid]; | ||
| 325 | const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), | ||
| 326 | [&sdl_joystick](const auto& joystick) { | ||
| 327 | return joystick->GetSDLJoystick() == sdl_joystick; | ||
| 328 | }); | ||
| 329 | |||
| 330 | if (joystick_it != joystick_guid_list.end()) { | ||
| 331 | (*joystick_it)->SetSDLJoystick(nullptr, nullptr); | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { | ||
| 336 | switch (event.type) { | ||
| 337 | case SDL_JOYBUTTONUP: { | ||
| 338 | if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { | ||
| 339 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | ||
| 340 | SetButton(identifier, event.jbutton.button, false); | ||
| 341 | } | ||
| 342 | break; | ||
| 343 | } | ||
| 344 | case SDL_JOYBUTTONDOWN: { | ||
| 345 | if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { | ||
| 346 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | ||
| 347 | SetButton(identifier, event.jbutton.button, true); | ||
| 348 | } | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | case SDL_JOYHATMOTION: { | ||
| 352 | if (const auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) { | ||
| 353 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | ||
| 354 | SetHatButton(identifier, event.jhat.hat, event.jhat.value); | ||
| 355 | } | ||
| 356 | break; | ||
| 357 | } | ||
| 358 | case SDL_JOYAXISMOTION: { | ||
| 359 | if (const auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) { | ||
| 360 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | ||
| 361 | SetAxis(identifier, event.jaxis.axis, event.jaxis.value / 32767.0f); | ||
| 362 | } | ||
| 363 | break; | ||
| 364 | } | ||
| 365 | case SDL_CONTROLLERSENSORUPDATE: { | ||
| 366 | if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) { | ||
| 367 | if (joystick->UpdateMotion(event.csensor)) { | ||
| 368 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | ||
| 369 | SetMotion(identifier, 0, joystick->GetMotion()); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | break; | ||
| 373 | } | ||
| 374 | case SDL_JOYDEVICEREMOVED: | ||
| 375 | LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); | ||
| 376 | CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); | ||
| 377 | break; | ||
| 378 | case SDL_JOYDEVICEADDED: | ||
| 379 | LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which); | ||
| 380 | InitJoystick(event.jdevice.which); | ||
| 381 | break; | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | void SDLDriver::CloseJoysticks() { | ||
| 386 | std::lock_guard lock{joystick_map_mutex}; | ||
| 387 | joystick_map.clear(); | ||
| 388 | } | ||
| 389 | |||
| 390 | SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) { | ||
| 391 | if (!Settings::values.enable_raw_input) { | ||
| 392 | // Disable raw input. When enabled this setting causes SDL to die when a web applet opens | ||
| 393 | SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0"); | ||
| 394 | } | ||
| 395 | |||
| 396 | // Prevent SDL from adding undesired axis | ||
| 397 | SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); | ||
| 398 | |||
| 399 | // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers | ||
| 400 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); | ||
| 401 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); | ||
| 402 | SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); | ||
| 403 | |||
| 404 | // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and | ||
| 405 | // not a generic one | ||
| 406 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); | ||
| 407 | |||
| 408 | // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native | ||
| 409 | // driver on Linux. | ||
| 410 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0"); | ||
| 411 | |||
| 412 | // If the frontend is going to manage the event loop, then we don't start one here | ||
| 413 | start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == 0; | ||
| 414 | if (start_thread && SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { | ||
| 415 | LOG_CRITICAL(Input, "SDL_Init failed with: {}", SDL_GetError()); | ||
| 416 | return; | ||
| 417 | } | ||
| 418 | |||
| 419 | SDL_AddEventWatch(&SDLEventWatcher, this); | ||
| 420 | |||
| 421 | initialized = true; | ||
| 422 | if (start_thread) { | ||
| 423 | poll_thread = std::thread([this] { | ||
| 424 | Common::SetCurrentThreadName("yuzu:input:SDL"); | ||
| 425 | using namespace std::chrono_literals; | ||
| 426 | while (initialized) { | ||
| 427 | SDL_PumpEvents(); | ||
| 428 | std::this_thread::sleep_for(1ms); | ||
| 429 | } | ||
| 430 | }); | ||
| 431 | } | ||
| 432 | // Because the events for joystick connection happens before we have our event watcher added, we | ||
| 433 | // can just open all the joysticks right here | ||
| 434 | for (int i = 0; i < SDL_NumJoysticks(); ++i) { | ||
| 435 | InitJoystick(i); | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | SDLDriver::~SDLDriver() { | ||
| 440 | CloseJoysticks(); | ||
| 441 | SDL_DelEventWatch(&SDLEventWatcher, this); | ||
| 442 | |||
| 443 | initialized = false; | ||
| 444 | if (start_thread) { | ||
| 445 | poll_thread.join(); | ||
| 446 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); | ||
| 447 | } | ||
| 448 | } | ||
| 449 | |||
| 450 | std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const { | ||
| 451 | std::vector<Common::ParamPackage> devices; | ||
| 452 | std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs; | ||
| 453 | for (const auto& [key, value] : joystick_map) { | ||
| 454 | for (const auto& joystick : value) { | ||
| 455 | if (!joystick->GetSDLJoystick()) { | ||
| 456 | continue; | ||
| 457 | } | ||
| 458 | const std::string name = | ||
| 459 | fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort()); | ||
| 460 | devices.emplace_back(Common::ParamPackage{ | ||
| 461 | {"engine", GetEngineName()}, | ||
| 462 | {"display", std::move(name)}, | ||
| 463 | {"guid", joystick->GetGUID()}, | ||
| 464 | {"port", std::to_string(joystick->GetPort())}, | ||
| 465 | }); | ||
| 466 | if (joystick->IsJoyconLeft()) { | ||
| 467 | joycon_pairs.insert_or_assign(joystick->GetPort(), joystick); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | // Add dual controllers | ||
| 473 | for (const auto& [key, value] : joystick_map) { | ||
| 474 | for (const auto& joystick : value) { | ||
| 475 | if (joystick->IsJoyconRight()) { | ||
| 476 | if (!joycon_pairs.contains(joystick->GetPort())) { | ||
| 477 | continue; | ||
| 478 | } | ||
| 479 | const auto joystick2 = joycon_pairs.at(joystick->GetPort()); | ||
| 480 | |||
| 481 | const std::string name = | ||
| 482 | fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort()); | ||
| 483 | devices.emplace_back(Common::ParamPackage{ | ||
| 484 | {"engine", GetEngineName()}, | ||
| 485 | {"display", std::move(name)}, | ||
| 486 | {"guid", joystick->GetGUID()}, | ||
| 487 | {"guid2", joystick2->GetGUID()}, | ||
| 488 | {"port", std::to_string(joystick->GetPort())}, | ||
| 489 | }); | ||
| 490 | } | ||
| 491 | } | ||
| 492 | } | ||
| 493 | return devices; | ||
| 494 | } | ||
| 495 | |||
| 496 | Common::Input::VibrationError SDLDriver::SetRumble( | ||
| 497 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { | ||
| 498 | const auto joystick = | ||
| 499 | GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port)); | ||
| 500 | const auto process_amplitude_exp = [](f32 amplitude, f32 factor) { | ||
| 501 | return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF; | ||
| 502 | }; | ||
| 503 | |||
| 504 | // Default exponential curve for rumble | ||
| 505 | f32 factor = 0.35f; | ||
| 506 | |||
| 507 | // If vibration is set as a linear output use a flatter value | ||
| 508 | if (vibration.type == Common::Input::VibrationAmplificationType::Linear) { | ||
| 509 | factor = 0.5f; | ||
| 510 | } | ||
| 511 | |||
| 512 | // Amplitude for HD rumble needs no modification | ||
| 513 | if (joystick->HasHDRumble()) { | ||
| 514 | factor = 1.0f; | ||
| 515 | } | ||
| 516 | |||
| 517 | const Common::Input::VibrationStatus new_vibration{ | ||
| 518 | .low_amplitude = process_amplitude_exp(vibration.low_amplitude, factor), | ||
| 519 | .low_frequency = vibration.low_frequency, | ||
| 520 | .high_amplitude = process_amplitude_exp(vibration.high_amplitude, factor), | ||
| 521 | .high_frequency = vibration.high_frequency, | ||
| 522 | .type = Common::Input::VibrationAmplificationType::Exponential, | ||
| 523 | }; | ||
| 524 | |||
| 525 | if (!joystick->RumblePlay(new_vibration)) { | ||
| 526 | return Common::Input::VibrationError::Unknown; | ||
| 527 | } | ||
| 528 | |||
| 529 | return Common::Input::VibrationError::None; | ||
| 530 | } | ||
| 531 | |||
| 532 | Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid, | ||
| 533 | s32 axis, float value) const { | ||
| 534 | Common::ParamPackage params{}; | ||
| 535 | params.Set("engine", GetEngineName()); | ||
| 536 | params.Set("port", port); | ||
| 537 | params.Set("guid", std::move(guid)); | ||
| 538 | params.Set("axis", axis); | ||
| 539 | params.Set("threshold", "0.5"); | ||
| 540 | params.Set("invert", value < 0 ? "-" : "+"); | ||
| 541 | return params; | ||
| 542 | } | ||
| 543 | |||
| 544 | Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, std::string guid, | ||
| 545 | s32 button) const { | ||
| 546 | Common::ParamPackage params{}; | ||
| 547 | params.Set("engine", GetEngineName()); | ||
| 548 | params.Set("port", port); | ||
| 549 | params.Set("guid", std::move(guid)); | ||
| 550 | params.Set("button", button); | ||
| 551 | return params; | ||
| 552 | } | ||
| 553 | |||
| 554 | Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, std::string guid, s32 hat, | ||
| 555 | u8 value) const { | ||
| 556 | Common::ParamPackage params{}; | ||
| 557 | params.Set("engine", GetEngineName()); | ||
| 558 | params.Set("port", port); | ||
| 559 | params.Set("guid", std::move(guid)); | ||
| 560 | params.Set("hat", hat); | ||
| 561 | params.Set("direction", GetHatButtonName(value)); | ||
| 562 | return params; | ||
| 563 | } | ||
| 564 | |||
| 565 | Common::ParamPackage SDLDriver::BuildMotionParam(int port, std::string guid) const { | ||
| 566 | Common::ParamPackage params{}; | ||
| 567 | params.Set("engine", GetEngineName()); | ||
| 568 | params.Set("motion", 0); | ||
| 569 | params.Set("port", port); | ||
| 570 | params.Set("guid", std::move(guid)); | ||
| 571 | return params; | ||
| 572 | } | ||
| 573 | |||
| 574 | Common::ParamPackage SDLDriver::BuildParamPackageForBinding( | ||
| 575 | int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const { | ||
| 576 | switch (binding.bindType) { | ||
| 577 | case SDL_CONTROLLER_BINDTYPE_NONE: | ||
| 578 | break; | ||
| 579 | case SDL_CONTROLLER_BINDTYPE_AXIS: | ||
| 580 | return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); | ||
| 581 | case SDL_CONTROLLER_BINDTYPE_BUTTON: | ||
| 582 | return BuildButtonParamPackageForButton(port, guid, binding.value.button); | ||
| 583 | case SDL_CONTROLLER_BINDTYPE_HAT: | ||
| 584 | return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat, | ||
| 585 | static_cast<u8>(binding.value.hat.hat_mask)); | ||
| 586 | } | ||
| 587 | return {}; | ||
| 588 | } | ||
| 589 | |||
| 590 | Common::ParamPackage SDLDriver::BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, | ||
| 591 | int axis_y, float offset_x, | ||
| 592 | float offset_y) const { | ||
| 593 | Common::ParamPackage params; | ||
| 594 | params.Set("engine", GetEngineName()); | ||
| 595 | params.Set("port", static_cast<int>(identifier.port)); | ||
| 596 | params.Set("guid", identifier.guid.Format()); | ||
| 597 | params.Set("axis_x", axis_x); | ||
| 598 | params.Set("axis_y", axis_y); | ||
| 599 | params.Set("offset_x", offset_x); | ||
| 600 | params.Set("offset_y", offset_y); | ||
| 601 | params.Set("invert_x", "+"); | ||
| 602 | params.Set("invert_y", "+"); | ||
| 603 | return params; | ||
| 604 | } | ||
| 605 | |||
| 606 | ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& params) { | ||
| 607 | if (!params.Has("guid") || !params.Has("port")) { | ||
| 608 | return {}; | ||
| 609 | } | ||
| 610 | const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); | ||
| 611 | |||
| 612 | auto* controller = joystick->GetSDLGameController(); | ||
| 613 | if (controller == nullptr) { | ||
| 614 | return {}; | ||
| 615 | } | ||
| 616 | |||
| 617 | // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. | ||
| 618 | // We will add those afterwards | ||
| 619 | // This list also excludes Screenshot since theres not really a mapping for that | ||
| 620 | ButtonBindings switch_to_sdl_button; | ||
| 621 | |||
| 622 | if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { | ||
| 623 | switch_to_sdl_button = GetNintendoButtonBinding(joystick); | ||
| 624 | } else { | ||
| 625 | switch_to_sdl_button = GetDefaultButtonBinding(); | ||
| 626 | } | ||
| 627 | |||
| 628 | // Add the missing bindings for ZL/ZR | ||
| 629 | static constexpr ZButtonBindings switch_to_sdl_axis{{ | ||
| 630 | {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, | ||
| 631 | {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, | ||
| 632 | }}; | ||
| 633 | |||
| 634 | // Parameters contain two joysticks return dual | ||
| 635 | if (params.Has("guid2")) { | ||
| 636 | const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); | ||
| 637 | |||
| 638 | if (joystick2->GetSDLGameController() != nullptr) { | ||
| 639 | return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button, | ||
| 640 | switch_to_sdl_axis); | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis); | ||
| 645 | } | ||
| 646 | |||
| 647 | ButtonBindings SDLDriver::GetDefaultButtonBinding() const { | ||
| 648 | return { | ||
| 649 | std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, | ||
| 650 | {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, | ||
| 651 | {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, | ||
| 652 | {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, | ||
| 653 | {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, | ||
| 654 | {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, | ||
| 655 | {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, | ||
| 656 | {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, | ||
| 657 | {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, | ||
| 658 | {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, | ||
| 659 | {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, | ||
| 660 | {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, | ||
| 661 | {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, | ||
| 662 | {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, | ||
| 663 | {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, | ||
| 664 | {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, | ||
| 665 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, | ||
| 666 | }; | ||
| 667 | } | ||
| 668 | |||
| 669 | ButtonBindings SDLDriver::GetNintendoButtonBinding( | ||
| 670 | const std::shared_ptr<SDLJoystick>& joystick) const { | ||
| 671 | // Default SL/SR mapping for pro controllers | ||
| 672 | auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; | ||
| 673 | auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; | ||
| 674 | |||
| 675 | if (joystick->IsJoyconLeft()) { | ||
| 676 | sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; | ||
| 677 | sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; | ||
| 678 | } | ||
| 679 | if (joystick->IsJoyconRight()) { | ||
| 680 | sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; | ||
| 681 | sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; | ||
| 682 | } | ||
| 683 | |||
| 684 | return { | ||
| 685 | std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A}, | ||
| 686 | {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B}, | ||
| 687 | {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X}, | ||
| 688 | {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y}, | ||
| 689 | {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, | ||
| 690 | {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, | ||
| 691 | {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, | ||
| 692 | {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, | ||
| 693 | {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, | ||
| 694 | {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, | ||
| 695 | {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, | ||
| 696 | {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, | ||
| 697 | {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, | ||
| 698 | {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, | ||
| 699 | {Settings::NativeButton::SL, sl_button}, | ||
| 700 | {Settings::NativeButton::SR, sr_button}, | ||
| 701 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, | ||
| 702 | }; | ||
| 703 | } | ||
| 704 | |||
| 705 | ButtonMapping SDLDriver::GetSingleControllerMapping( | ||
| 706 | const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button, | ||
| 707 | const ZButtonBindings& switch_to_sdl_axis) const { | ||
| 708 | ButtonMapping mapping; | ||
| 709 | mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); | ||
| 710 | auto* controller = joystick->GetSDLGameController(); | ||
| 711 | |||
| 712 | for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { | ||
| 713 | const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); | ||
| 714 | mapping.insert_or_assign( | ||
| 715 | switch_button, | ||
| 716 | BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); | ||
| 717 | } | ||
| 718 | for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { | ||
| 719 | const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); | ||
| 720 | mapping.insert_or_assign( | ||
| 721 | switch_button, | ||
| 722 | BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); | ||
| 723 | } | ||
| 724 | |||
| 725 | return mapping; | ||
| 726 | } | ||
| 727 | |||
| 728 | ButtonMapping SDLDriver::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, | ||
| 729 | const std::shared_ptr<SDLJoystick>& joystick2, | ||
| 730 | const ButtonBindings& switch_to_sdl_button, | ||
| 731 | const ZButtonBindings& switch_to_sdl_axis) const { | ||
| 732 | ButtonMapping mapping; | ||
| 733 | mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); | ||
| 734 | auto* controller = joystick->GetSDLGameController(); | ||
| 735 | auto* controller2 = joystick2->GetSDLGameController(); | ||
| 736 | |||
| 737 | for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { | ||
| 738 | if (IsButtonOnLeftSide(switch_button)) { | ||
| 739 | const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button); | ||
| 740 | mapping.insert_or_assign( | ||
| 741 | switch_button, | ||
| 742 | BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); | ||
| 743 | continue; | ||
| 744 | } | ||
| 745 | const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); | ||
| 746 | mapping.insert_or_assign( | ||
| 747 | switch_button, | ||
| 748 | BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); | ||
| 749 | } | ||
| 750 | for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { | ||
| 751 | if (IsButtonOnLeftSide(switch_button)) { | ||
| 752 | const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis); | ||
| 753 | mapping.insert_or_assign( | ||
| 754 | switch_button, | ||
| 755 | BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); | ||
| 756 | continue; | ||
| 757 | } | ||
| 758 | const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); | ||
| 759 | mapping.insert_or_assign( | ||
| 760 | switch_button, | ||
| 761 | BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); | ||
| 762 | } | ||
| 763 | |||
| 764 | return mapping; | ||
| 765 | } | ||
| 766 | |||
| 767 | bool SDLDriver::IsButtonOnLeftSide(Settings::NativeButton::Values button) const { | ||
| 768 | switch (button) { | ||
| 769 | case Settings::NativeButton::DDown: | ||
| 770 | case Settings::NativeButton::DLeft: | ||
| 771 | case Settings::NativeButton::DRight: | ||
| 772 | case Settings::NativeButton::DUp: | ||
| 773 | case Settings::NativeButton::L: | ||
| 774 | case Settings::NativeButton::LStick: | ||
| 775 | case Settings::NativeButton::Minus: | ||
| 776 | case Settings::NativeButton::Screenshot: | ||
| 777 | case Settings::NativeButton::ZL: | ||
| 778 | return true; | ||
| 779 | default: | ||
| 780 | return false; | ||
| 781 | } | ||
| 782 | } | ||
| 783 | |||
| 784 | AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& params) { | ||
| 785 | if (!params.Has("guid") || !params.Has("port")) { | ||
| 786 | return {}; | ||
| 787 | } | ||
| 788 | const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); | ||
| 789 | const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); | ||
| 790 | auto* controller = joystick->GetSDLGameController(); | ||
| 791 | if (controller == nullptr) { | ||
| 792 | return {}; | ||
| 793 | } | ||
| 794 | |||
| 795 | AnalogMapping mapping = {}; | ||
| 796 | const auto& binding_left_x = | ||
| 797 | SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); | ||
| 798 | const auto& binding_left_y = | ||
| 799 | SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); | ||
| 800 | if (params.Has("guid2")) { | ||
| 801 | const auto identifier = joystick2->GetPadIdentifier(); | ||
| 802 | PreSetController(identifier); | ||
| 803 | PreSetAxis(identifier, binding_left_x.value.axis); | ||
| 804 | PreSetAxis(identifier, binding_left_y.value.axis); | ||
| 805 | const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); | ||
| 806 | const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis); | ||
| 807 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, | ||
| 808 | BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, | ||
| 809 | binding_left_y.value.axis, | ||
| 810 | left_offset_x, left_offset_y)); | ||
| 811 | } else { | ||
| 812 | const auto identifier = joystick->GetPadIdentifier(); | ||
| 813 | PreSetController(identifier); | ||
| 814 | PreSetAxis(identifier, binding_left_x.value.axis); | ||
| 815 | PreSetAxis(identifier, binding_left_y.value.axis); | ||
| 816 | const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); | ||
| 817 | const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis); | ||
| 818 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, | ||
| 819 | BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, | ||
| 820 | binding_left_y.value.axis, | ||
| 821 | left_offset_x, left_offset_y)); | ||
| 822 | } | ||
| 823 | const auto& binding_right_x = | ||
| 824 | SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); | ||
| 825 | const auto& binding_right_y = | ||
| 826 | SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); | ||
| 827 | const auto identifier = joystick->GetPadIdentifier(); | ||
| 828 | PreSetController(identifier); | ||
| 829 | PreSetAxis(identifier, binding_right_x.value.axis); | ||
| 830 | PreSetAxis(identifier, binding_right_y.value.axis); | ||
| 831 | const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis); | ||
| 832 | const auto right_offset_y = -GetAxis(identifier, binding_right_y.value.axis); | ||
| 833 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, | ||
| 834 | BuildParamPackageForAnalog(identifier, binding_right_x.value.axis, | ||
| 835 | binding_right_y.value.axis, right_offset_x, | ||
| 836 | right_offset_y)); | ||
| 837 | return mapping; | ||
| 838 | } | ||
| 839 | |||
| 840 | MotionMapping SDLDriver::GetMotionMappingForDevice(const Common::ParamPackage& params) { | ||
| 841 | if (!params.Has("guid") || !params.Has("port")) { | ||
| 842 | return {}; | ||
| 843 | } | ||
| 844 | const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); | ||
| 845 | const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); | ||
| 846 | auto* controller = joystick->GetSDLGameController(); | ||
| 847 | if (controller == nullptr) { | ||
| 848 | return {}; | ||
| 849 | } | ||
| 850 | |||
| 851 | MotionMapping mapping = {}; | ||
| 852 | joystick->EnableMotion(); | ||
| 853 | |||
| 854 | if (joystick->HasGyro() || joystick->HasAccel()) { | ||
| 855 | mapping.insert_or_assign(Settings::NativeMotion::MotionRight, | ||
| 856 | BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); | ||
| 857 | } | ||
| 858 | if (params.Has("guid2")) { | ||
| 859 | joystick2->EnableMotion(); | ||
| 860 | if (joystick2->HasGyro() || joystick2->HasAccel()) { | ||
| 861 | mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, | ||
| 862 | BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID())); | ||
| 863 | } | ||
| 864 | } else { | ||
| 865 | if (joystick->HasGyro() || joystick->HasAccel()) { | ||
| 866 | mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, | ||
| 867 | BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); | ||
| 868 | } | ||
| 869 | } | ||
| 870 | |||
| 871 | return mapping; | ||
| 872 | } | ||
| 873 | |||
| 874 | Common::Input::ButtonNames SDLDriver::GetUIName(const Common::ParamPackage& params) const { | ||
| 875 | if (params.Has("button")) { | ||
| 876 | // TODO(German77): Find how to substitue the values for real button names | ||
| 877 | return Common::Input::ButtonNames::Value; | ||
| 878 | } | ||
| 879 | if (params.Has("hat")) { | ||
| 880 | return Common::Input::ButtonNames::Value; | ||
| 881 | } | ||
| 882 | if (params.Has("axis")) { | ||
| 883 | return Common::Input::ButtonNames::Value; | ||
| 884 | } | ||
| 885 | if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) { | ||
| 886 | return Common::Input::ButtonNames::Value; | ||
| 887 | } | ||
| 888 | if (params.Has("motion")) { | ||
| 889 | return Common::Input::ButtonNames::Engine; | ||
| 890 | } | ||
| 891 | |||
| 892 | return Common::Input::ButtonNames::Invalid; | ||
| 893 | } | ||
| 894 | |||
| 895 | std::string SDLDriver::GetHatButtonName(u8 direction_value) const { | ||
| 896 | switch (direction_value) { | ||
| 897 | case SDL_HAT_UP: | ||
| 898 | return "up"; | ||
| 899 | case SDL_HAT_DOWN: | ||
| 900 | return "down"; | ||
| 901 | case SDL_HAT_LEFT: | ||
| 902 | return "left"; | ||
| 903 | case SDL_HAT_RIGHT: | ||
| 904 | return "right"; | ||
| 905 | default: | ||
| 906 | return {}; | ||
| 907 | } | ||
| 908 | } | ||
| 909 | |||
| 910 | u8 SDLDriver::GetHatButtonId(const std::string& direction_name) const { | ||
| 911 | Uint8 direction; | ||
| 912 | if (direction_name == "up") { | ||
| 913 | direction = SDL_HAT_UP; | ||
| 914 | } else if (direction_name == "down") { | ||
| 915 | direction = SDL_HAT_DOWN; | ||
| 916 | } else if (direction_name == "left") { | ||
| 917 | direction = SDL_HAT_LEFT; | ||
| 918 | } else if (direction_name == "right") { | ||
| 919 | direction = SDL_HAT_RIGHT; | ||
| 920 | } else { | ||
| 921 | direction = 0; | ||
| 922 | } | ||
| 923 | return direction; | ||
| 924 | } | ||
| 925 | |||
| 926 | } // namespace InputCommon | ||
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/drivers/sdl_driver.h index 7a9ad6346..e9a5d2e26 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/drivers/sdl_driver.h | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <memory> | ||
| 9 | #include <mutex> | 8 | #include <mutex> |
| 10 | #include <thread> | 9 | #include <thread> |
| 11 | #include <unordered_map> | 10 | #include <unordered_map> |
| @@ -13,34 +12,29 @@ | |||
| 13 | #include <SDL.h> | 12 | #include <SDL.h> |
| 14 | 13 | ||
| 15 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 16 | #include "common/threadsafe_queue.h" | 15 | #include "input_common/input_engine.h" |
| 17 | #include "input_common/sdl/sdl.h" | ||
| 18 | 16 | ||
| 19 | union SDL_Event; | 17 | union SDL_Event; |
| 20 | using SDL_GameController = struct _SDL_GameController; | 18 | using SDL_GameController = struct _SDL_GameController; |
| 21 | using SDL_Joystick = struct _SDL_Joystick; | 19 | using SDL_Joystick = struct _SDL_Joystick; |
| 22 | using SDL_JoystickID = s32; | 20 | using SDL_JoystickID = s32; |
| 23 | 21 | ||
| 22 | namespace InputCommon { | ||
| 23 | |||
| 24 | class SDLJoystick; | ||
| 25 | |||
| 24 | using ButtonBindings = | 26 | using ButtonBindings = |
| 25 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; | 27 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; |
| 26 | using ZButtonBindings = | 28 | using ZButtonBindings = |
| 27 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; | 29 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; |
| 28 | 30 | ||
| 29 | namespace InputCommon::SDL { | 31 | class SDLDriver : public InputEngine { |
| 30 | |||
| 31 | class SDLAnalogFactory; | ||
| 32 | class SDLButtonFactory; | ||
| 33 | class SDLMotionFactory; | ||
| 34 | class SDLVibrationFactory; | ||
| 35 | class SDLJoystick; | ||
| 36 | |||
| 37 | class SDLState : public State { | ||
| 38 | public: | 32 | public: |
| 39 | /// Initializes and registers SDL device factories | 33 | /// Initializes and registers SDL device factories |
| 40 | SDLState(); | 34 | explicit SDLDriver(std::string input_engine_); |
| 41 | 35 | ||
| 42 | /// Unregisters SDL device factories and shut them down. | 36 | /// Unregisters SDL device factories and shut them down. |
| 43 | ~SDLState() override; | 37 | ~SDLDriver() override; |
| 44 | 38 | ||
| 45 | /// Handle SDL_Events for joysticks from SDL_PollEvent | 39 | /// Handle SDL_Events for joysticks from SDL_PollEvent |
| 46 | void HandleGameControllerEvent(const SDL_Event& event); | 40 | void HandleGameControllerEvent(const SDL_Event& event); |
| @@ -54,18 +48,18 @@ public: | |||
| 54 | */ | 48 | */ |
| 55 | std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); | 49 | std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); |
| 56 | 50 | ||
| 57 | /// Get all DevicePoller that use the SDL backend for a specific device type | 51 | std::vector<Common::ParamPackage> GetInputDevices() const override; |
| 58 | Pollers GetPollers(Polling::DeviceType type) override; | ||
| 59 | |||
| 60 | /// Used by the Pollers during config | ||
| 61 | std::atomic<bool> polling = false; | ||
| 62 | Common::SPSCQueue<SDL_Event> event_queue; | ||
| 63 | |||
| 64 | std::vector<Common::ParamPackage> GetInputDevices() override; | ||
| 65 | 52 | ||
| 66 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; | 53 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; |
| 67 | AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; | 54 | AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; |
| 68 | MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override; | 55 | MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override; |
| 56 | Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; | ||
| 57 | |||
| 58 | std::string GetHatButtonName(u8 direction_value) const override; | ||
| 59 | u8 GetHatButtonId(const std::string& direction_name) const override; | ||
| 60 | |||
| 61 | Common::Input::VibrationError SetRumble( | ||
| 62 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | ||
| 69 | 63 | ||
| 70 | private: | 64 | private: |
| 71 | void InitJoystick(int joystick_index); | 65 | void InitJoystick(int joystick_index); |
| @@ -74,6 +68,23 @@ private: | |||
| 74 | /// Needs to be called before SDL_QuitSubSystem. | 68 | /// Needs to be called before SDL_QuitSubSystem. |
| 75 | void CloseJoysticks(); | 69 | void CloseJoysticks(); |
| 76 | 70 | ||
| 71 | Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, | ||
| 72 | float value = 0.1f) const; | ||
| 73 | Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, | ||
| 74 | s32 button) const; | ||
| 75 | |||
| 76 | Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, | ||
| 77 | u8 value) const; | ||
| 78 | |||
| 79 | Common::ParamPackage BuildMotionParam(int port, std::string guid) const; | ||
| 80 | |||
| 81 | Common::ParamPackage BuildParamPackageForBinding( | ||
| 82 | int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const; | ||
| 83 | |||
| 84 | Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, | ||
| 85 | int axis_y, float offset_x, | ||
| 86 | float offset_y) const; | ||
| 87 | |||
| 77 | /// Returns the default button bindings list for generic controllers | 88 | /// Returns the default button bindings list for generic controllers |
| 78 | ButtonBindings GetDefaultButtonBinding() const; | 89 | ButtonBindings GetDefaultButtonBinding() const; |
| 79 | 90 | ||
| @@ -94,21 +105,13 @@ private: | |||
| 94 | /// Returns true if the button is on the left joycon | 105 | /// Returns true if the button is on the left joycon |
| 95 | bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; | 106 | bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; |
| 96 | 107 | ||
| 97 | // Set to true if SDL supports game controller subsystem | ||
| 98 | bool has_gamecontroller = false; | ||
| 99 | |||
| 100 | /// Map of GUID of a list of corresponding virtual Joysticks | 108 | /// Map of GUID of a list of corresponding virtual Joysticks |
| 101 | std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; | 109 | std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; |
| 102 | std::mutex joystick_map_mutex; | 110 | std::mutex joystick_map_mutex; |
| 103 | 111 | ||
| 104 | std::shared_ptr<SDLButtonFactory> button_factory; | ||
| 105 | std::shared_ptr<SDLAnalogFactory> analog_factory; | ||
| 106 | std::shared_ptr<SDLVibrationFactory> vibration_factory; | ||
| 107 | std::shared_ptr<SDLMotionFactory> motion_factory; | ||
| 108 | |||
| 109 | bool start_thread = false; | 112 | bool start_thread = false; |
| 110 | std::atomic<bool> initialized = false; | 113 | std::atomic<bool> initialized = false; |
| 111 | 114 | ||
| 112 | std::thread poll_thread; | 115 | std::thread poll_thread; |
| 113 | }; | 116 | }; |
| 114 | } // namespace InputCommon::SDL | 117 | } // namespace InputCommon |
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp new file mode 100644 index 000000000..5bdd5dac3 --- /dev/null +++ b/src/input_common/drivers/tas_input.cpp | |||
| @@ -0,0 +1,320 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <fmt/format.h> | ||
| 7 | |||
| 8 | #include "common/fs/file.h" | ||
| 9 | #include "common/fs/fs_types.h" | ||
| 10 | #include "common/fs/path_util.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "common/settings.h" | ||
| 13 | #include "input_common/drivers/tas_input.h" | ||
| 14 | |||
| 15 | namespace InputCommon::TasInput { | ||
| 16 | |||
| 17 | enum class Tas::TasAxis : u8 { | ||
| 18 | StickX, | ||
| 19 | StickY, | ||
| 20 | SubstickX, | ||
| 21 | SubstickY, | ||
| 22 | Undefined, | ||
| 23 | }; | ||
| 24 | |||
| 25 | // Supported keywords and buttons from a TAS file | ||
| 26 | constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = { | ||
| 27 | std::pair{"KEY_A", TasButton::BUTTON_A}, | ||
| 28 | {"KEY_B", TasButton::BUTTON_B}, | ||
| 29 | {"KEY_X", TasButton::BUTTON_X}, | ||
| 30 | {"KEY_Y", TasButton::BUTTON_Y}, | ||
| 31 | {"KEY_LSTICK", TasButton::STICK_L}, | ||
| 32 | {"KEY_RSTICK", TasButton::STICK_R}, | ||
| 33 | {"KEY_L", TasButton::TRIGGER_L}, | ||
| 34 | {"KEY_R", TasButton::TRIGGER_R}, | ||
| 35 | {"KEY_PLUS", TasButton::BUTTON_PLUS}, | ||
| 36 | {"KEY_MINUS", TasButton::BUTTON_MINUS}, | ||
| 37 | {"KEY_DLEFT", TasButton::BUTTON_LEFT}, | ||
| 38 | {"KEY_DUP", TasButton::BUTTON_UP}, | ||
| 39 | {"KEY_DRIGHT", TasButton::BUTTON_RIGHT}, | ||
| 40 | {"KEY_DDOWN", TasButton::BUTTON_DOWN}, | ||
| 41 | {"KEY_SL", TasButton::BUTTON_SL}, | ||
| 42 | {"KEY_SR", TasButton::BUTTON_SR}, | ||
| 43 | {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE}, | ||
| 44 | {"KEY_HOME", TasButton::BUTTON_HOME}, | ||
| 45 | {"KEY_ZL", TasButton::TRIGGER_ZL}, | ||
| 46 | {"KEY_ZR", TasButton::TRIGGER_ZR}, | ||
| 47 | }; | ||
| 48 | |||
| 49 | Tas::Tas(std::string input_engine_) : InputEngine(std::move(input_engine_)) { | ||
| 50 | for (size_t player_index = 0; player_index < PLAYER_NUMBER; player_index++) { | ||
| 51 | PadIdentifier identifier{ | ||
| 52 | .guid = Common::UUID{}, | ||
| 53 | .port = player_index, | ||
| 54 | .pad = 0, | ||
| 55 | }; | ||
| 56 | PreSetController(identifier); | ||
| 57 | } | ||
| 58 | ClearInput(); | ||
| 59 | if (!Settings::values.tas_enable) { | ||
| 60 | needs_reset = true; | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | LoadTasFiles(); | ||
| 64 | } | ||
| 65 | |||
| 66 | Tas::~Tas() { | ||
| 67 | Stop(); | ||
| 68 | } | ||
| 69 | |||
| 70 | void Tas::LoadTasFiles() { | ||
| 71 | script_length = 0; | ||
| 72 | for (size_t i = 0; i < commands.size(); i++) { | ||
| 73 | LoadTasFile(i, 0); | ||
| 74 | if (commands[i].size() > script_length) { | ||
| 75 | script_length = commands[i].size(); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | void Tas::LoadTasFile(size_t player_index, size_t file_index) { | ||
| 81 | commands[player_index].clear(); | ||
| 82 | |||
| 83 | std::string file = Common::FS::ReadStringFromFile( | ||
| 84 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / | ||
| 85 | fmt::format("script{}-{}.txt", file_index, player_index + 1), | ||
| 86 | Common::FS::FileType::BinaryFile); | ||
| 87 | std::istringstream command_line(file); | ||
| 88 | std::string line; | ||
| 89 | int frame_no = 0; | ||
| 90 | while (std::getline(command_line, line, '\n')) { | ||
| 91 | if (line.empty()) { | ||
| 92 | continue; | ||
| 93 | } | ||
| 94 | |||
| 95 | std::vector<std::string> seg_list; | ||
| 96 | { | ||
| 97 | std::istringstream line_stream(line); | ||
| 98 | std::string segment; | ||
| 99 | while (std::getline(line_stream, segment, ' ')) { | ||
| 100 | seg_list.push_back(std::move(segment)); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | if (seg_list.size() < 4) { | ||
| 105 | continue; | ||
| 106 | } | ||
| 107 | |||
| 108 | const auto num_frames = std::stoi(seg_list[0]); | ||
| 109 | while (frame_no < num_frames) { | ||
| 110 | commands[player_index].emplace_back(); | ||
| 111 | frame_no++; | ||
| 112 | } | ||
| 113 | |||
| 114 | TASCommand command = { | ||
| 115 | .buttons = ReadCommandButtons(seg_list[1]), | ||
| 116 | .l_axis = ReadCommandAxis(seg_list[2]), | ||
| 117 | .r_axis = ReadCommandAxis(seg_list[3]), | ||
| 118 | }; | ||
| 119 | commands[player_index].push_back(command); | ||
| 120 | frame_no++; | ||
| 121 | } | ||
| 122 | LOG_INFO(Input, "TAS file loaded! {} frames", frame_no); | ||
| 123 | } | ||
| 124 | |||
| 125 | void Tas::WriteTasFile(std::u8string_view file_name) { | ||
| 126 | std::string output_text; | ||
| 127 | for (size_t frame = 0; frame < record_commands.size(); frame++) { | ||
| 128 | const TASCommand& line = record_commands[frame]; | ||
| 129 | output_text += fmt::format("{} {} {} {}\n", frame, WriteCommandButtons(line.buttons), | ||
| 130 | WriteCommandAxis(line.l_axis), WriteCommandAxis(line.r_axis)); | ||
| 131 | } | ||
| 132 | |||
| 133 | const auto tas_file_name = Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name; | ||
| 134 | const auto bytes_written = | ||
| 135 | Common::FS::WriteStringToFile(tas_file_name, Common::FS::FileType::TextFile, output_text); | ||
| 136 | if (bytes_written == output_text.size()) { | ||
| 137 | LOG_INFO(Input, "TAS file written to file!"); | ||
| 138 | } else { | ||
| 139 | LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written, | ||
| 140 | output_text.size()); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) { | ||
| 145 | last_input = { | ||
| 146 | .buttons = buttons, | ||
| 147 | .l_axis = left_axis, | ||
| 148 | .r_axis = right_axis, | ||
| 149 | }; | ||
| 150 | } | ||
| 151 | |||
| 152 | std::tuple<TasState, size_t, size_t> Tas::GetStatus() const { | ||
| 153 | TasState state; | ||
| 154 | if (is_recording) { | ||
| 155 | return {TasState::Recording, 0, record_commands.size()}; | ||
| 156 | } | ||
| 157 | |||
| 158 | if (is_running) { | ||
| 159 | state = TasState::Running; | ||
| 160 | } else { | ||
| 161 | state = TasState::Stopped; | ||
| 162 | } | ||
| 163 | |||
| 164 | return {state, current_command, script_length}; | ||
| 165 | } | ||
| 166 | |||
| 167 | void Tas::UpdateThread() { | ||
| 168 | if (!Settings::values.tas_enable) { | ||
| 169 | if (is_running) { | ||
| 170 | Stop(); | ||
| 171 | } | ||
| 172 | return; | ||
| 173 | } | ||
| 174 | |||
| 175 | if (is_recording) { | ||
| 176 | record_commands.push_back(last_input); | ||
| 177 | } | ||
| 178 | if (needs_reset) { | ||
| 179 | current_command = 0; | ||
| 180 | needs_reset = false; | ||
| 181 | LoadTasFiles(); | ||
| 182 | LOG_DEBUG(Input, "tas_reset done"); | ||
| 183 | } | ||
| 184 | |||
| 185 | if (!is_running) { | ||
| 186 | ClearInput(); | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | if (current_command < script_length) { | ||
| 190 | LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length); | ||
| 191 | const size_t frame = current_command++; | ||
| 192 | for (size_t player_index = 0; player_index < commands.size(); player_index++) { | ||
| 193 | TASCommand command{}; | ||
| 194 | if (frame < commands[player_index].size()) { | ||
| 195 | command = commands[player_index][frame]; | ||
| 196 | } | ||
| 197 | |||
| 198 | PadIdentifier identifier{ | ||
| 199 | .guid = Common::UUID{}, | ||
| 200 | .port = player_index, | ||
| 201 | .pad = 0, | ||
| 202 | }; | ||
| 203 | for (std::size_t i = 0; i < sizeof(command.buttons) * 8; ++i) { | ||
| 204 | const bool button_status = (command.buttons & (1LLU << i)) != 0; | ||
| 205 | const int button = static_cast<int>(i); | ||
| 206 | SetButton(identifier, button, button_status); | ||
| 207 | } | ||
| 208 | SetTasAxis(identifier, TasAxis::StickX, command.l_axis.x); | ||
| 209 | SetTasAxis(identifier, TasAxis::StickY, command.l_axis.y); | ||
| 210 | SetTasAxis(identifier, TasAxis::SubstickX, command.r_axis.x); | ||
| 211 | SetTasAxis(identifier, TasAxis::SubstickY, command.r_axis.y); | ||
| 212 | } | ||
| 213 | } else { | ||
| 214 | is_running = Settings::values.tas_loop.GetValue(); | ||
| 215 | LoadTasFiles(); | ||
| 216 | current_command = 0; | ||
| 217 | ClearInput(); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | void Tas::ClearInput() { | ||
| 222 | ResetButtonState(); | ||
| 223 | ResetAnalogState(); | ||
| 224 | } | ||
| 225 | |||
| 226 | TasAnalog Tas::ReadCommandAxis(const std::string& line) const { | ||
| 227 | std::vector<std::string> seg_list; | ||
| 228 | { | ||
| 229 | std::istringstream line_stream(line); | ||
| 230 | std::string segment; | ||
| 231 | while (std::getline(line_stream, segment, ';')) { | ||
| 232 | seg_list.push_back(std::move(segment)); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | const float x = std::stof(seg_list.at(0)) / 32767.0f; | ||
| 237 | const float y = std::stof(seg_list.at(1)) / 32767.0f; | ||
| 238 | |||
| 239 | return {x, y}; | ||
| 240 | } | ||
| 241 | |||
| 242 | u64 Tas::ReadCommandButtons(const std::string& line) const { | ||
| 243 | std::istringstream button_text(line); | ||
| 244 | std::string button_line; | ||
| 245 | u64 buttons = 0; | ||
| 246 | while (std::getline(button_text, button_line, ';')) { | ||
| 247 | for (const auto& [text, tas_button] : text_to_tas_button) { | ||
| 248 | if (text == button_line) { | ||
| 249 | buttons |= static_cast<u64>(tas_button); | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | return buttons; | ||
| 255 | } | ||
| 256 | |||
| 257 | std::string Tas::WriteCommandButtons(u64 buttons) const { | ||
| 258 | std::string returns; | ||
| 259 | for (const auto& [text_button, tas_button] : text_to_tas_button) { | ||
| 260 | if ((buttons & static_cast<u64>(tas_button)) != 0) { | ||
| 261 | returns += fmt::format("{};", text_button); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | return returns.empty() ? "NONE" : returns; | ||
| 265 | } | ||
| 266 | |||
| 267 | std::string Tas::WriteCommandAxis(TasAnalog analog) const { | ||
| 268 | return fmt::format("{};{}", analog.x * 32767, analog.y * 32767); | ||
| 269 | } | ||
| 270 | |||
| 271 | void Tas::SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value) { | ||
| 272 | SetAxis(identifier, static_cast<int>(axis), value); | ||
| 273 | } | ||
| 274 | |||
| 275 | void Tas::StartStop() { | ||
| 276 | if (!Settings::values.tas_enable) { | ||
| 277 | return; | ||
| 278 | } | ||
| 279 | if (is_running) { | ||
| 280 | Stop(); | ||
| 281 | } else { | ||
| 282 | is_running = true; | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | void Tas::Stop() { | ||
| 287 | is_running = false; | ||
| 288 | } | ||
| 289 | |||
| 290 | void Tas::Reset() { | ||
| 291 | if (!Settings::values.tas_enable) { | ||
| 292 | return; | ||
| 293 | } | ||
| 294 | needs_reset = true; | ||
| 295 | } | ||
| 296 | |||
| 297 | bool Tas::Record() { | ||
| 298 | if (!Settings::values.tas_enable) { | ||
| 299 | return true; | ||
| 300 | } | ||
| 301 | is_recording = !is_recording; | ||
| 302 | return is_recording; | ||
| 303 | } | ||
| 304 | |||
| 305 | void Tas::SaveRecording(bool overwrite_file) { | ||
| 306 | if (is_recording) { | ||
| 307 | return; | ||
| 308 | } | ||
| 309 | if (record_commands.empty()) { | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | WriteTasFile(u8"record.txt"); | ||
| 313 | if (overwrite_file) { | ||
| 314 | WriteTasFile(u8"script0-1.txt"); | ||
| 315 | } | ||
| 316 | needs_reset = true; | ||
| 317 | record_commands.clear(); | ||
| 318 | } | ||
| 319 | |||
| 320 | } // namespace InputCommon::TasInput | ||
diff --git a/src/input_common/drivers/tas_input.h b/src/input_common/drivers/tas_input.h new file mode 100644 index 000000000..4b4e6c417 --- /dev/null +++ b/src/input_common/drivers/tas_input.h | |||
| @@ -0,0 +1,201 @@ | |||
| 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 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "input_common/input_engine.h" | ||
| 13 | |||
| 14 | /* | ||
| 15 | To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below | ||
| 16 | Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt | ||
| 17 | for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players). | ||
| 18 | |||
| 19 | A script file has the same format as TAS-nx uses, so final files will look like this: | ||
| 20 | |||
| 21 | 1 KEY_B 0;0 0;0 | ||
| 22 | 6 KEY_ZL 0;0 0;0 | ||
| 23 | 41 KEY_ZL;KEY_Y 0;0 0;0 | ||
| 24 | 43 KEY_X;KEY_A 32767;0 0;0 | ||
| 25 | 44 KEY_A 32767;0 0;0 | ||
| 26 | 45 KEY_A 32767;0 0;0 | ||
| 27 | 46 KEY_A 32767;0 0;0 | ||
| 28 | 47 KEY_A 32767;0 0;0 | ||
| 29 | |||
| 30 | After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey | ||
| 31 | CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file | ||
| 32 | has. Playback can be started or stopped using CTRL+F5. | ||
| 33 | |||
| 34 | However, for playback to actually work, the correct input device has to be selected: In the Controls | ||
| 35 | menu, select TAS from the device list for the controller that the script should be played on. | ||
| 36 | |||
| 37 | Recording a new script file is really simple: Just make sure that the proper device (not TAS) is | ||
| 38 | connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke | ||
| 39 | again (CTRL+F7). The new script will be saved at the location previously selected, as the filename | ||
| 40 | record.txt. | ||
| 41 | |||
| 42 | For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller | ||
| 43 | P1). | ||
| 44 | */ | ||
| 45 | |||
| 46 | namespace InputCommon::TasInput { | ||
| 47 | |||
| 48 | constexpr size_t PLAYER_NUMBER = 10; | ||
| 49 | |||
| 50 | enum class TasButton : u64 { | ||
| 51 | BUTTON_A = 1U << 0, | ||
| 52 | BUTTON_B = 1U << 1, | ||
| 53 | BUTTON_X = 1U << 2, | ||
| 54 | BUTTON_Y = 1U << 3, | ||
| 55 | STICK_L = 1U << 4, | ||
| 56 | STICK_R = 1U << 5, | ||
| 57 | TRIGGER_L = 1U << 6, | ||
| 58 | TRIGGER_R = 1U << 7, | ||
| 59 | TRIGGER_ZL = 1U << 8, | ||
| 60 | TRIGGER_ZR = 1U << 9, | ||
| 61 | BUTTON_PLUS = 1U << 10, | ||
| 62 | BUTTON_MINUS = 1U << 11, | ||
| 63 | BUTTON_LEFT = 1U << 12, | ||
| 64 | BUTTON_UP = 1U << 13, | ||
| 65 | BUTTON_RIGHT = 1U << 14, | ||
| 66 | BUTTON_DOWN = 1U << 15, | ||
| 67 | BUTTON_SL = 1U << 16, | ||
| 68 | BUTTON_SR = 1U << 17, | ||
| 69 | BUTTON_HOME = 1U << 18, | ||
| 70 | BUTTON_CAPTURE = 1U << 19, | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct TasAnalog { | ||
| 74 | float x{}; | ||
| 75 | float y{}; | ||
| 76 | }; | ||
| 77 | |||
| 78 | enum class TasState { | ||
| 79 | Running, | ||
| 80 | Recording, | ||
| 81 | Stopped, | ||
| 82 | }; | ||
| 83 | |||
| 84 | class Tas final : public InputEngine { | ||
| 85 | public: | ||
| 86 | explicit Tas(std::string input_engine_); | ||
| 87 | ~Tas() override; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Changes the input status that will be stored in each frame | ||
| 91 | * @param buttons Bitfield with the status of the buttons | ||
| 92 | * @param left_axis Value of the left axis | ||
| 93 | * @param right_axis Value of the right axis | ||
| 94 | */ | ||
| 95 | void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis); | ||
| 96 | |||
| 97 | // Main loop that records or executes input | ||
| 98 | void UpdateThread(); | ||
| 99 | |||
| 100 | // Sets the flag to start or stop the TAS command execution and swaps controllers profiles | ||
| 101 | void StartStop(); | ||
| 102 | |||
| 103 | // Stop the TAS and reverts any controller profile | ||
| 104 | void Stop(); | ||
| 105 | |||
| 106 | // Sets the flag to reload the file and start from the beginning in the next update | ||
| 107 | void Reset(); | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Sets the flag to enable or disable recording of inputs | ||
| 111 | * @returns true if the current recording status is enabled | ||
| 112 | */ | ||
| 113 | bool Record(); | ||
| 114 | |||
| 115 | /** | ||
| 116 | * Saves contents of record_commands on a file | ||
| 117 | * @param overwrite_file Indicates if player 1 should be overwritten | ||
| 118 | */ | ||
| 119 | void SaveRecording(bool overwrite_file); | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Returns the current status values of TAS playback/recording | ||
| 123 | * @returns A Tuple of | ||
| 124 | * TasState indicating the current state out of Running ; | ||
| 125 | * Current playback progress ; | ||
| 126 | * Total length of script file currently loaded or being recorded | ||
| 127 | */ | ||
| 128 | std::tuple<TasState, size_t, size_t> GetStatus() const; | ||
| 129 | |||
| 130 | private: | ||
| 131 | enum class TasAxis : u8; | ||
| 132 | |||
| 133 | struct TASCommand { | ||
| 134 | u64 buttons{}; | ||
| 135 | TasAnalog l_axis{}; | ||
| 136 | TasAnalog r_axis{}; | ||
| 137 | }; | ||
| 138 | |||
| 139 | /// Loads TAS files from all players | ||
| 140 | void LoadTasFiles(); | ||
| 141 | |||
| 142 | /** | ||
| 143 | * Loads TAS file from the specified player | ||
| 144 | * @param player_index Player number to save the script | ||
| 145 | * @param file_index Script number of the file | ||
| 146 | */ | ||
| 147 | void LoadTasFile(size_t player_index, size_t file_index); | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Writes a TAS file from the recorded commands | ||
| 151 | * @param file_name Name of the file to be written | ||
| 152 | */ | ||
| 153 | void WriteTasFile(std::u8string_view file_name); | ||
| 154 | |||
| 155 | /** | ||
| 156 | * Parses a string containing the axis values. X and Y have a range from -32767 to 32767 | ||
| 157 | * @param line String containing axis values with the following format "x;y" | ||
| 158 | * @returns A TAS analog object with axis values with range from -1.0 to 1.0 | ||
| 159 | */ | ||
| 160 | TasAnalog ReadCommandAxis(const std::string& line) const; | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Parses a string containing the button values. Each button is represented by it's text format | ||
| 164 | * specified in text_to_tas_button array | ||
| 165 | * @param line string containing button name with the following format "a;b;c;d..." | ||
| 166 | * @returns A u64 with each bit representing the status of a button | ||
| 167 | */ | ||
| 168 | u64 ReadCommandButtons(const std::string& line) const; | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Reset state of all players | ||
| 172 | */ | ||
| 173 | void ClearInput(); | ||
| 174 | |||
| 175 | /** | ||
| 176 | * Converts an u64 containing the button status into the text equivalent | ||
| 177 | * @param buttons Bitfield with the status of the buttons | ||
| 178 | * @returns A string with the name of the buttons to be written to the file | ||
| 179 | */ | ||
| 180 | std::string WriteCommandButtons(u64 buttons) const; | ||
| 181 | |||
| 182 | /** | ||
| 183 | * Converts an TAS analog object containing the axis status into the text equivalent | ||
| 184 | * @param analog Value of the axis | ||
| 185 | * @returns A string with the value of the axis to be written to the file | ||
| 186 | */ | ||
| 187 | std::string WriteCommandAxis(TasAnalog analog) const; | ||
| 188 | |||
| 189 | /// Sets an axis for a particular pad to the given value. | ||
| 190 | void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value); | ||
| 191 | |||
| 192 | size_t script_length{0}; | ||
| 193 | bool is_recording{false}; | ||
| 194 | bool is_running{false}; | ||
| 195 | bool needs_reset{false}; | ||
| 196 | std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{}; | ||
| 197 | std::vector<TASCommand> record_commands{}; | ||
| 198 | size_t current_command{0}; | ||
| 199 | TASCommand last_input{}; // only used for recording | ||
| 200 | }; | ||
| 201 | } // namespace InputCommon::TasInput | ||
diff --git a/src/input_common/drivers/touch_screen.cpp b/src/input_common/drivers/touch_screen.cpp new file mode 100644 index 000000000..880781825 --- /dev/null +++ b/src/input_common/drivers/touch_screen.cpp | |||
| @@ -0,0 +1,53 @@ | |||
| 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/param_package.h" | ||
| 6 | #include "input_common/drivers/touch_screen.h" | ||
| 7 | |||
| 8 | namespace InputCommon { | ||
| 9 | |||
| 10 | constexpr PadIdentifier identifier = { | ||
| 11 | .guid = Common::UUID{Common::INVALID_UUID}, | ||
| 12 | .port = 0, | ||
| 13 | .pad = 0, | ||
| 14 | }; | ||
| 15 | |||
| 16 | TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) { | ||
| 17 | PreSetController(identifier); | ||
| 18 | } | ||
| 19 | |||
| 20 | void TouchScreen::TouchMoved(float x, float y, std::size_t finger) { | ||
| 21 | if (finger >= 16) { | ||
| 22 | return; | ||
| 23 | } | ||
| 24 | TouchPressed(x, y, finger); | ||
| 25 | } | ||
| 26 | |||
| 27 | void TouchScreen::TouchPressed(float x, float y, std::size_t finger) { | ||
| 28 | if (finger >= 16) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | SetButton(identifier, static_cast<int>(finger), true); | ||
| 32 | SetAxis(identifier, static_cast<int>(finger * 2), x); | ||
| 33 | SetAxis(identifier, static_cast<int>(finger * 2 + 1), y); | ||
| 34 | } | ||
| 35 | |||
| 36 | void TouchScreen::TouchReleased(std::size_t finger) { | ||
| 37 | if (finger >= 16) { | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | SetButton(identifier, static_cast<int>(finger), false); | ||
| 41 | SetAxis(identifier, static_cast<int>(finger * 2), 0.0f); | ||
| 42 | SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f); | ||
| 43 | } | ||
| 44 | |||
| 45 | void TouchScreen::ReleaseAllTouch() { | ||
| 46 | for (int index = 0; index < 16; ++index) { | ||
| 47 | SetButton(identifier, index, false); | ||
| 48 | SetAxis(identifier, index * 2, 0.0f); | ||
| 49 | SetAxis(identifier, index * 2 + 1, 0.0f); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/touch_screen.h b/src/input_common/drivers/touch_screen.h new file mode 100644 index 000000000..bf395c40b --- /dev/null +++ b/src/input_common/drivers/touch_screen.h | |||
| @@ -0,0 +1,44 @@ | |||
| 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 "input_common/input_engine.h" | ||
| 8 | |||
| 9 | namespace InputCommon { | ||
| 10 | |||
| 11 | /** | ||
| 12 | * A button device factory representing a keyboard. It receives keyboard events and forward them | ||
| 13 | * to all button devices it created. | ||
| 14 | */ | ||
| 15 | class TouchScreen final : public InputEngine { | ||
| 16 | public: | ||
| 17 | explicit TouchScreen(std::string input_engine_); | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Signals that mouse has moved. | ||
| 21 | * @param x the x-coordinate of the cursor | ||
| 22 | * @param y the y-coordinate of the cursor | ||
| 23 | * @param center_x the x-coordinate of the middle of the screen | ||
| 24 | * @param center_y the y-coordinate of the middle of the screen | ||
| 25 | */ | ||
| 26 | void TouchMoved(float x, float y, std::size_t finger); | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Sets the status of all buttons bound with the key to pressed | ||
| 30 | * @param key_code the code of the key to press | ||
| 31 | */ | ||
| 32 | void TouchPressed(float x, float y, std::size_t finger); | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Sets the status of all buttons bound with the key to released | ||
| 36 | * @param key_code the code of the key to release | ||
| 37 | */ | ||
| 38 | void TouchReleased(std::size_t finger); | ||
| 39 | |||
| 40 | /// Resets all inputs to their initial value | ||
| 41 | void ReleaseAllTouch(); | ||
| 42 | }; | ||
| 43 | |||
| 44 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp new file mode 100644 index 000000000..4ab991a7d --- /dev/null +++ b/src/input_common/drivers/udp_client.cpp | |||
| @@ -0,0 +1,591 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <random> | ||
| 6 | #include <boost/asio.hpp> | ||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | #include "common/logging/log.h" | ||
| 10 | #include "common/param_package.h" | ||
| 11 | #include "common/settings.h" | ||
| 12 | #include "input_common/drivers/udp_client.h" | ||
| 13 | #include "input_common/helpers/udp_protocol.h" | ||
| 14 | |||
| 15 | using boost::asio::ip::udp; | ||
| 16 | |||
| 17 | namespace InputCommon::CemuhookUDP { | ||
| 18 | |||
| 19 | struct SocketCallback { | ||
| 20 | std::function<void(Response::Version)> version; | ||
| 21 | std::function<void(Response::PortInfo)> port_info; | ||
| 22 | std::function<void(Response::PadData)> pad_data; | ||
| 23 | }; | ||
| 24 | |||
| 25 | class Socket { | ||
| 26 | public: | ||
| 27 | using clock = std::chrono::system_clock; | ||
| 28 | |||
| 29 | explicit Socket(const std::string& host, u16 port, SocketCallback callback_) | ||
| 30 | : callback(std::move(callback_)), timer(io_service), | ||
| 31 | socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) { | ||
| 32 | boost::system::error_code ec{}; | ||
| 33 | auto ipv4 = boost::asio::ip::make_address_v4(host, ec); | ||
| 34 | if (ec.value() != boost::system::errc::success) { | ||
| 35 | LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host); | ||
| 36 | ipv4 = boost::asio::ip::address_v4{}; | ||
| 37 | } | ||
| 38 | |||
| 39 | send_endpoint = {udp::endpoint(ipv4, port)}; | ||
| 40 | } | ||
| 41 | |||
| 42 | void Stop() { | ||
| 43 | io_service.stop(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void Loop() { | ||
| 47 | io_service.run(); | ||
| 48 | } | ||
| 49 | |||
| 50 | void StartSend(const clock::time_point& from) { | ||
| 51 | timer.expires_at(from + std::chrono::seconds(3)); | ||
| 52 | timer.async_wait([this](const boost::system::error_code& error) { HandleSend(error); }); | ||
| 53 | } | ||
| 54 | |||
| 55 | void StartReceive() { | ||
| 56 | socket.async_receive_from( | ||
| 57 | boost::asio::buffer(receive_buffer), receive_endpoint, | ||
| 58 | [this](const boost::system::error_code& error, std::size_t bytes_transferred) { | ||
| 59 | HandleReceive(error, bytes_transferred); | ||
| 60 | }); | ||
| 61 | } | ||
| 62 | |||
| 63 | private: | ||
| 64 | u32 GenerateRandomClientId() const { | ||
| 65 | std::random_device device; | ||
| 66 | return device(); | ||
| 67 | } | ||
| 68 | |||
| 69 | void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) { | ||
| 70 | if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { | ||
| 71 | switch (*type) { | ||
| 72 | case Type::Version: { | ||
| 73 | Response::Version version; | ||
| 74 | std::memcpy(&version, &receive_buffer[sizeof(Header)], sizeof(Response::Version)); | ||
| 75 | callback.version(std::move(version)); | ||
| 76 | break; | ||
| 77 | } | ||
| 78 | case Type::PortInfo: { | ||
| 79 | Response::PortInfo port_info; | ||
| 80 | std::memcpy(&port_info, &receive_buffer[sizeof(Header)], | ||
| 81 | sizeof(Response::PortInfo)); | ||
| 82 | callback.port_info(std::move(port_info)); | ||
| 83 | break; | ||
| 84 | } | ||
| 85 | case Type::PadData: { | ||
| 86 | Response::PadData pad_data; | ||
| 87 | std::memcpy(&pad_data, &receive_buffer[sizeof(Header)], sizeof(Response::PadData)); | ||
| 88 | callback.pad_data(std::move(pad_data)); | ||
| 89 | break; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | StartReceive(); | ||
| 94 | } | ||
| 95 | |||
| 96 | void HandleSend(const boost::system::error_code&) { | ||
| 97 | boost::system::error_code _ignored{}; | ||
| 98 | // Send a request for getting port info for the pad | ||
| 99 | const Request::PortInfo port_info{4, {0, 1, 2, 3}}; | ||
| 100 | const auto port_message = Request::Create(port_info, client_id); | ||
| 101 | std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); | ||
| 102 | socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored); | ||
| 103 | |||
| 104 | // Send a request for getting pad data for the pad | ||
| 105 | const Request::PadData pad_data{ | ||
| 106 | Request::RegisterFlags::AllPads, | ||
| 107 | 0, | ||
| 108 | EMPTY_MAC_ADDRESS, | ||
| 109 | }; | ||
| 110 | const auto pad_message = Request::Create(pad_data, client_id); | ||
| 111 | std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE); | ||
| 112 | socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored); | ||
| 113 | StartSend(timer.expiry()); | ||
| 114 | } | ||
| 115 | |||
| 116 | SocketCallback callback; | ||
| 117 | boost::asio::io_service io_service; | ||
| 118 | boost::asio::basic_waitable_timer<clock> timer; | ||
| 119 | udp::socket socket; | ||
| 120 | |||
| 121 | const u32 client_id; | ||
| 122 | |||
| 123 | static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); | ||
| 124 | static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>); | ||
| 125 | std::array<u8, PORT_INFO_SIZE> send_buffer1; | ||
| 126 | std::array<u8, PAD_DATA_SIZE> send_buffer2; | ||
| 127 | udp::endpoint send_endpoint; | ||
| 128 | |||
| 129 | std::array<u8, MAX_PACKET_SIZE> receive_buffer; | ||
| 130 | udp::endpoint receive_endpoint; | ||
| 131 | }; | ||
| 132 | |||
| 133 | static void SocketLoop(Socket* socket) { | ||
| 134 | socket->StartReceive(); | ||
| 135 | socket->StartSend(Socket::clock::now()); | ||
| 136 | socket->Loop(); | ||
| 137 | } | ||
| 138 | |||
| 139 | UDPClient::UDPClient(std::string input_engine_) : InputEngine(std::move(input_engine_)) { | ||
| 140 | LOG_INFO(Input, "Udp Initialization started"); | ||
| 141 | ReloadSockets(); | ||
| 142 | } | ||
| 143 | |||
| 144 | UDPClient::~UDPClient() { | ||
| 145 | Reset(); | ||
| 146 | } | ||
| 147 | |||
| 148 | UDPClient::ClientConnection::ClientConnection() = default; | ||
| 149 | |||
| 150 | UDPClient::ClientConnection::~ClientConnection() = default; | ||
| 151 | |||
| 152 | void UDPClient::ReloadSockets() { | ||
| 153 | Reset(); | ||
| 154 | |||
| 155 | std::stringstream servers_ss(Settings::values.udp_input_servers.GetValue()); | ||
| 156 | std::string server_token; | ||
| 157 | std::size_t client = 0; | ||
| 158 | while (std::getline(servers_ss, server_token, ',')) { | ||
| 159 | if (client == MAX_UDP_CLIENTS) { | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | std::stringstream server_ss(server_token); | ||
| 163 | std::string token; | ||
| 164 | std::getline(server_ss, token, ':'); | ||
| 165 | std::string udp_input_address = token; | ||
| 166 | std::getline(server_ss, token, ':'); | ||
| 167 | char* temp; | ||
| 168 | const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0)); | ||
| 169 | if (*temp != '\0') { | ||
| 170 | LOG_ERROR(Input, "Port number is not valid {}", token); | ||
| 171 | continue; | ||
| 172 | } | ||
| 173 | |||
| 174 | const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port); | ||
| 175 | if (client_number != MAX_UDP_CLIENTS) { | ||
| 176 | LOG_ERROR(Input, "Duplicated UDP servers found"); | ||
| 177 | continue; | ||
| 178 | } | ||
| 179 | StartCommunication(client++, udp_input_address, udp_input_port); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const { | ||
| 184 | for (std::size_t client = 0; client < clients.size(); client++) { | ||
| 185 | if (clients[client].active == -1) { | ||
| 186 | continue; | ||
| 187 | } | ||
| 188 | if (clients[client].host == host && clients[client].port == port) { | ||
| 189 | return client; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | return MAX_UDP_CLIENTS; | ||
| 193 | } | ||
| 194 | |||
| 195 | void UDPClient::OnVersion([[maybe_unused]] Response::Version data) { | ||
| 196 | LOG_TRACE(Input, "Version packet received: {}", data.version); | ||
| 197 | } | ||
| 198 | |||
| 199 | void UDPClient::OnPortInfo([[maybe_unused]] Response::PortInfo data) { | ||
| 200 | LOG_TRACE(Input, "PortInfo packet received: {}", data.model); | ||
| 201 | } | ||
| 202 | |||
| 203 | void UDPClient::OnPadData(Response::PadData data, std::size_t client) { | ||
| 204 | const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id; | ||
| 205 | |||
| 206 | if (pad_index >= pads.size()) { | ||
| 207 | LOG_ERROR(Input, "Invalid pad id {}", data.info.id); | ||
| 208 | return; | ||
| 209 | } | ||
| 210 | |||
| 211 | LOG_TRACE(Input, "PadData packet received"); | ||
| 212 | if (data.packet_counter == pads[pad_index].packet_sequence) { | ||
| 213 | LOG_WARNING( | ||
| 214 | Input, | ||
| 215 | "PadData packet dropped because its stale info. Current count: {} Packet count: {}", | ||
| 216 | pads[pad_index].packet_sequence, data.packet_counter); | ||
| 217 | pads[pad_index].connected = false; | ||
| 218 | return; | ||
| 219 | } | ||
| 220 | |||
| 221 | clients[client].active = 1; | ||
| 222 | pads[pad_index].connected = true; | ||
| 223 | pads[pad_index].packet_sequence = data.packet_counter; | ||
| 224 | |||
| 225 | const auto now = std::chrono::steady_clock::now(); | ||
| 226 | const auto time_difference = static_cast<u64>( | ||
| 227 | std::chrono::duration_cast<std::chrono::microseconds>(now - pads[pad_index].last_update) | ||
| 228 | .count()); | ||
| 229 | pads[pad_index].last_update = now; | ||
| 230 | |||
| 231 | // Gyroscope values are not it the correct scale from better joy. | ||
| 232 | // Dividing by 312 allows us to make one full turn = 1 turn | ||
| 233 | // This must be a configurable valued called sensitivity | ||
| 234 | const float gyro_scale = 1.0f / 312.0f; | ||
| 235 | |||
| 236 | const BasicMotion motion{ | ||
| 237 | .gyro_x = data.gyro.pitch * gyro_scale, | ||
| 238 | .gyro_y = data.gyro.roll * gyro_scale, | ||
| 239 | .gyro_z = -data.gyro.yaw * gyro_scale, | ||
| 240 | .accel_x = data.accel.x, | ||
| 241 | .accel_y = -data.accel.z, | ||
| 242 | .accel_z = data.accel.y, | ||
| 243 | .delta_timestamp = time_difference, | ||
| 244 | }; | ||
| 245 | const PadIdentifier identifier = GetPadIdentifier(pad_index); | ||
| 246 | SetMotion(identifier, 0, motion); | ||
| 247 | |||
| 248 | for (std::size_t id = 0; id < data.touch.size(); ++id) { | ||
| 249 | const auto touch_pad = data.touch[id]; | ||
| 250 | const auto touch_axis_x_id = | ||
| 251 | static_cast<int>(id == 0 ? PadAxes::Touch1X : PadAxes::Touch2X); | ||
| 252 | const auto touch_axis_y_id = | ||
| 253 | static_cast<int>(id == 0 ? PadAxes::Touch1Y : PadAxes::Touch2Y); | ||
| 254 | const auto touch_button_id = | ||
| 255 | static_cast<int>(id == 0 ? PadButton::Touch1 : PadButton::touch2); | ||
| 256 | |||
| 257 | // TODO: Use custom calibration per device | ||
| 258 | const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); | ||
| 259 | const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100)); | ||
| 260 | const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50)); | ||
| 261 | const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800)); | ||
| 262 | const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850)); | ||
| 263 | |||
| 264 | const f32 x = | ||
| 265 | static_cast<f32>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) / | ||
| 266 | static_cast<f32>(max_x - min_x); | ||
| 267 | const f32 y = | ||
| 268 | static_cast<f32>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) / | ||
| 269 | static_cast<f32>(max_y - min_y); | ||
| 270 | |||
| 271 | if (touch_pad.is_active) { | ||
| 272 | SetAxis(identifier, touch_axis_x_id, x); | ||
| 273 | SetAxis(identifier, touch_axis_y_id, y); | ||
| 274 | SetButton(identifier, touch_button_id, true); | ||
| 275 | continue; | ||
| 276 | } | ||
| 277 | SetAxis(identifier, touch_axis_x_id, 0); | ||
| 278 | SetAxis(identifier, touch_axis_y_id, 0); | ||
| 279 | SetButton(identifier, touch_button_id, false); | ||
| 280 | } | ||
| 281 | |||
| 282 | SetAxis(identifier, static_cast<int>(PadAxes::LeftStickX), | ||
| 283 | (data.left_stick_x - 127.0f) / 127.0f); | ||
| 284 | SetAxis(identifier, static_cast<int>(PadAxes::LeftStickY), | ||
| 285 | (data.left_stick_y - 127.0f) / 127.0f); | ||
| 286 | SetAxis(identifier, static_cast<int>(PadAxes::RightStickX), | ||
| 287 | (data.right_stick_x - 127.0f) / 127.0f); | ||
| 288 | SetAxis(identifier, static_cast<int>(PadAxes::RightStickY), | ||
| 289 | (data.right_stick_y - 127.0f) / 127.0f); | ||
| 290 | |||
| 291 | static constexpr std::array<PadButton, 16> buttons{ | ||
| 292 | PadButton::Share, PadButton::L3, PadButton::R3, PadButton::Options, | ||
| 293 | PadButton::Up, PadButton::Right, PadButton::Down, PadButton::Left, | ||
| 294 | PadButton::L2, PadButton::R2, PadButton::L1, PadButton::R1, | ||
| 295 | PadButton::Triangle, PadButton::Circle, PadButton::Cross, PadButton::Square}; | ||
| 296 | |||
| 297 | for (std::size_t i = 0; i < buttons.size(); ++i) { | ||
| 298 | const bool button_status = (data.digital_button & (1U << i)) != 0; | ||
| 299 | const int button = static_cast<int>(buttons[i]); | ||
| 300 | SetButton(identifier, button, button_status); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) { | ||
| 305 | SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, | ||
| 306 | [this](Response::PortInfo info) { OnPortInfo(info); }, | ||
| 307 | [this, client](Response::PadData data) { OnPadData(data, client); }}; | ||
| 308 | LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); | ||
| 309 | clients[client].uuid = GetHostUUID(host); | ||
| 310 | clients[client].host = host; | ||
| 311 | clients[client].port = port; | ||
| 312 | clients[client].active = 0; | ||
| 313 | clients[client].socket = std::make_unique<Socket>(host, port, callback); | ||
| 314 | clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; | ||
| 315 | for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) { | ||
| 316 | const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index); | ||
| 317 | PreSetController(identifier); | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | const PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const { | ||
| 322 | const std::size_t client = pad_index / PADS_PER_CLIENT; | ||
| 323 | return { | ||
| 324 | .guid = clients[client].uuid, | ||
| 325 | .port = static_cast<std::size_t>(clients[client].port), | ||
| 326 | .pad = pad_index, | ||
| 327 | }; | ||
| 328 | } | ||
| 329 | |||
| 330 | const Common::UUID UDPClient::GetHostUUID(const std::string host) const { | ||
| 331 | const auto ip = boost::asio::ip::address_v4::from_string(host); | ||
| 332 | const auto hex_host = fmt::format("{:06x}", ip.to_ulong()); | ||
| 333 | return Common::UUID{hex_host}; | ||
| 334 | } | ||
| 335 | |||
| 336 | void UDPClient::Reset() { | ||
| 337 | for (auto& client : clients) { | ||
| 338 | if (client.thread.joinable()) { | ||
| 339 | client.active = -1; | ||
| 340 | client.socket->Stop(); | ||
| 341 | client.thread.join(); | ||
| 342 | } | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const { | ||
| 347 | std::vector<Common::ParamPackage> devices; | ||
| 348 | if (!Settings::values.enable_udp_controller) { | ||
| 349 | return devices; | ||
| 350 | } | ||
| 351 | for (std::size_t client = 0; client < clients.size(); client++) { | ||
| 352 | if (clients[client].active != 1) { | ||
| 353 | continue; | ||
| 354 | } | ||
| 355 | for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) { | ||
| 356 | const std::size_t pad_index = client * PADS_PER_CLIENT + index; | ||
| 357 | if (!pads[pad_index].connected) { | ||
| 358 | continue; | ||
| 359 | } | ||
| 360 | const auto pad_identifier = GetPadIdentifier(pad_index); | ||
| 361 | Common::ParamPackage identifier{}; | ||
| 362 | identifier.Set("engine", GetEngineName()); | ||
| 363 | identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad)); | ||
| 364 | identifier.Set("guid", pad_identifier.guid.Format()); | ||
| 365 | identifier.Set("port", static_cast<int>(pad_identifier.port)); | ||
| 366 | identifier.Set("pad", static_cast<int>(pad_identifier.pad)); | ||
| 367 | devices.emplace_back(identifier); | ||
| 368 | } | ||
| 369 | } | ||
| 370 | return devices; | ||
| 371 | } | ||
| 372 | |||
| 373 | ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) { | ||
| 374 | // This list excludes any button that can't be really mapped | ||
| 375 | static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 18> | ||
| 376 | switch_to_dsu_button = { | ||
| 377 | std::pair{Settings::NativeButton::A, PadButton::Circle}, | ||
| 378 | {Settings::NativeButton::B, PadButton::Cross}, | ||
| 379 | {Settings::NativeButton::X, PadButton::Triangle}, | ||
| 380 | {Settings::NativeButton::Y, PadButton::Square}, | ||
| 381 | {Settings::NativeButton::Plus, PadButton::Options}, | ||
| 382 | {Settings::NativeButton::Minus, PadButton::Share}, | ||
| 383 | {Settings::NativeButton::DLeft, PadButton::Left}, | ||
| 384 | {Settings::NativeButton::DUp, PadButton::Up}, | ||
| 385 | {Settings::NativeButton::DRight, PadButton::Right}, | ||
| 386 | {Settings::NativeButton::DDown, PadButton::Down}, | ||
| 387 | {Settings::NativeButton::L, PadButton::L1}, | ||
| 388 | {Settings::NativeButton::R, PadButton::R1}, | ||
| 389 | {Settings::NativeButton::ZL, PadButton::L2}, | ||
| 390 | {Settings::NativeButton::ZR, PadButton::R2}, | ||
| 391 | {Settings::NativeButton::SL, PadButton::L2}, | ||
| 392 | {Settings::NativeButton::SR, PadButton::R2}, | ||
| 393 | {Settings::NativeButton::LStick, PadButton::L3}, | ||
| 394 | {Settings::NativeButton::RStick, PadButton::R3}, | ||
| 395 | }; | ||
| 396 | if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) { | ||
| 397 | return {}; | ||
| 398 | } | ||
| 399 | |||
| 400 | ButtonMapping mapping{}; | ||
| 401 | for (const auto& [switch_button, dsu_button] : switch_to_dsu_button) { | ||
| 402 | Common::ParamPackage button_params{}; | ||
| 403 | button_params.Set("engine", GetEngineName()); | ||
| 404 | button_params.Set("guid", params.Get("guid", "")); | ||
| 405 | button_params.Set("port", params.Get("port", 0)); | ||
| 406 | button_params.Set("pad", params.Get("pad", 0)); | ||
| 407 | button_params.Set("button", static_cast<int>(dsu_button)); | ||
| 408 | mapping.insert_or_assign(switch_button, std::move(button_params)); | ||
| 409 | } | ||
| 410 | |||
| 411 | return mapping; | ||
| 412 | } | ||
| 413 | |||
| 414 | AnalogMapping UDPClient::GetAnalogMappingForDevice(const Common::ParamPackage& params) { | ||
| 415 | if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) { | ||
| 416 | return {}; | ||
| 417 | } | ||
| 418 | |||
| 419 | AnalogMapping mapping = {}; | ||
| 420 | Common::ParamPackage left_analog_params; | ||
| 421 | left_analog_params.Set("engine", GetEngineName()); | ||
| 422 | left_analog_params.Set("guid", params.Get("guid", "")); | ||
| 423 | left_analog_params.Set("port", params.Get("port", 0)); | ||
| 424 | left_analog_params.Set("pad", params.Get("pad", 0)); | ||
| 425 | left_analog_params.Set("axis_x", static_cast<int>(PadAxes::LeftStickX)); | ||
| 426 | left_analog_params.Set("axis_y", static_cast<int>(PadAxes::LeftStickY)); | ||
| 427 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); | ||
| 428 | Common::ParamPackage right_analog_params; | ||
| 429 | right_analog_params.Set("engine", GetEngineName()); | ||
| 430 | right_analog_params.Set("guid", params.Get("guid", "")); | ||
| 431 | right_analog_params.Set("port", params.Get("port", 0)); | ||
| 432 | right_analog_params.Set("pad", params.Get("pad", 0)); | ||
| 433 | right_analog_params.Set("axis_x", static_cast<int>(PadAxes::RightStickX)); | ||
| 434 | right_analog_params.Set("axis_y", static_cast<int>(PadAxes::RightStickY)); | ||
| 435 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params)); | ||
| 436 | return mapping; | ||
| 437 | } | ||
| 438 | |||
| 439 | MotionMapping UDPClient::GetMotionMappingForDevice(const Common::ParamPackage& params) { | ||
| 440 | if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) { | ||
| 441 | return {}; | ||
| 442 | } | ||
| 443 | |||
| 444 | MotionMapping mapping = {}; | ||
| 445 | Common::ParamPackage motion_params; | ||
| 446 | motion_params.Set("engine", GetEngineName()); | ||
| 447 | motion_params.Set("guid", params.Get("guid", "")); | ||
| 448 | motion_params.Set("port", params.Get("port", 0)); | ||
| 449 | motion_params.Set("pad", params.Get("pad", 0)); | ||
| 450 | motion_params.Set("motion", 0); | ||
| 451 | mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(motion_params)); | ||
| 452 | mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(motion_params)); | ||
| 453 | return mapping; | ||
| 454 | } | ||
| 455 | |||
| 456 | Common::Input::ButtonNames UDPClient::GetUIButtonName(const Common::ParamPackage& params) const { | ||
| 457 | PadButton button = static_cast<PadButton>(params.Get("button", 0)); | ||
| 458 | switch (button) { | ||
| 459 | case PadButton::Left: | ||
| 460 | return Common::Input::ButtonNames::ButtonLeft; | ||
| 461 | case PadButton::Right: | ||
| 462 | return Common::Input::ButtonNames::ButtonRight; | ||
| 463 | case PadButton::Down: | ||
| 464 | return Common::Input::ButtonNames::ButtonDown; | ||
| 465 | case PadButton::Up: | ||
| 466 | return Common::Input::ButtonNames::ButtonUp; | ||
| 467 | case PadButton::L1: | ||
| 468 | return Common::Input::ButtonNames::L1; | ||
| 469 | case PadButton::L2: | ||
| 470 | return Common::Input::ButtonNames::L2; | ||
| 471 | case PadButton::L3: | ||
| 472 | return Common::Input::ButtonNames::L3; | ||
| 473 | case PadButton::R1: | ||
| 474 | return Common::Input::ButtonNames::R1; | ||
| 475 | case PadButton::R2: | ||
| 476 | return Common::Input::ButtonNames::R2; | ||
| 477 | case PadButton::R3: | ||
| 478 | return Common::Input::ButtonNames::R3; | ||
| 479 | case PadButton::Circle: | ||
| 480 | return Common::Input::ButtonNames::Circle; | ||
| 481 | case PadButton::Cross: | ||
| 482 | return Common::Input::ButtonNames::Cross; | ||
| 483 | case PadButton::Square: | ||
| 484 | return Common::Input::ButtonNames::Square; | ||
| 485 | case PadButton::Triangle: | ||
| 486 | return Common::Input::ButtonNames::Triangle; | ||
| 487 | case PadButton::Share: | ||
| 488 | return Common::Input::ButtonNames::Share; | ||
| 489 | case PadButton::Options: | ||
| 490 | return Common::Input::ButtonNames::Options; | ||
| 491 | default: | ||
| 492 | return Common::Input::ButtonNames::Undefined; | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | Common::Input::ButtonNames UDPClient::GetUIName(const Common::ParamPackage& params) const { | ||
| 497 | if (params.Has("button")) { | ||
| 498 | return GetUIButtonName(params); | ||
| 499 | } | ||
| 500 | if (params.Has("axis")) { | ||
| 501 | return Common::Input::ButtonNames::Value; | ||
| 502 | } | ||
| 503 | if (params.Has("motion")) { | ||
| 504 | return Common::Input::ButtonNames::Engine; | ||
| 505 | } | ||
| 506 | |||
| 507 | return Common::Input::ButtonNames::Invalid; | ||
| 508 | } | ||
| 509 | |||
| 510 | void TestCommunication(const std::string& host, u16 port, | ||
| 511 | const std::function<void()>& success_callback, | ||
| 512 | const std::function<void()>& failure_callback) { | ||
| 513 | std::thread([=] { | ||
| 514 | Common::Event success_event; | ||
| 515 | SocketCallback callback{ | ||
| 516 | .version = [](Response::Version) {}, | ||
| 517 | .port_info = [](Response::PortInfo) {}, | ||
| 518 | .pad_data = [&](Response::PadData) { success_event.Set(); }, | ||
| 519 | }; | ||
| 520 | Socket socket{host, port, std::move(callback)}; | ||
| 521 | std::thread worker_thread{SocketLoop, &socket}; | ||
| 522 | const bool result = | ||
| 523 | success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10)); | ||
| 524 | socket.Stop(); | ||
| 525 | worker_thread.join(); | ||
| 526 | if (result) { | ||
| 527 | success_callback(); | ||
| 528 | } else { | ||
| 529 | failure_callback(); | ||
| 530 | } | ||
| 531 | }).detach(); | ||
| 532 | } | ||
| 533 | |||
| 534 | CalibrationConfigurationJob::CalibrationConfigurationJob( | ||
| 535 | const std::string& host, u16 port, std::function<void(Status)> status_callback, | ||
| 536 | std::function<void(u16, u16, u16, u16)> data_callback) { | ||
| 537 | |||
| 538 | std::thread([=, this] { | ||
| 539 | Status current_status{Status::Initialized}; | ||
| 540 | SocketCallback callback{ | ||
| 541 | [](Response::Version) {}, [](Response::PortInfo) {}, | ||
| 542 | [&](Response::PadData data) { | ||
| 543 | static constexpr u16 CALIBRATION_THRESHOLD = 100; | ||
| 544 | static constexpr u16 MAX_VALUE = UINT16_MAX; | ||
| 545 | |||
| 546 | if (current_status == Status::Initialized) { | ||
| 547 | // Receiving data means the communication is ready now | ||
| 548 | current_status = Status::Ready; | ||
| 549 | status_callback(current_status); | ||
| 550 | } | ||
| 551 | const auto& touchpad_0 = data.touch[0]; | ||
| 552 | if (touchpad_0.is_active == 0) { | ||
| 553 | return; | ||
| 554 | } | ||
| 555 | LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y); | ||
| 556 | const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x)); | ||
| 557 | const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y)); | ||
| 558 | if (current_status == Status::Ready) { | ||
| 559 | // First touch - min data (min_x/min_y) | ||
| 560 | current_status = Status::Stage1Completed; | ||
| 561 | status_callback(current_status); | ||
| 562 | } | ||
| 563 | if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD && | ||
| 564 | touchpad_0.y - min_y > CALIBRATION_THRESHOLD) { | ||
| 565 | // Set the current position as max value and finishes configuration | ||
| 566 | const u16 max_x = touchpad_0.x; | ||
| 567 | const u16 max_y = touchpad_0.y; | ||
| 568 | current_status = Status::Completed; | ||
| 569 | data_callback(min_x, min_y, max_x, max_y); | ||
| 570 | status_callback(current_status); | ||
| 571 | |||
| 572 | complete_event.Set(); | ||
| 573 | } | ||
| 574 | }}; | ||
| 575 | Socket socket{host, port, std::move(callback)}; | ||
| 576 | std::thread worker_thread{SocketLoop, &socket}; | ||
| 577 | complete_event.Wait(); | ||
| 578 | socket.Stop(); | ||
| 579 | worker_thread.join(); | ||
| 580 | }).detach(); | ||
| 581 | } | ||
| 582 | |||
| 583 | CalibrationConfigurationJob::~CalibrationConfigurationJob() { | ||
| 584 | Stop(); | ||
| 585 | } | ||
| 586 | |||
| 587 | void CalibrationConfigurationJob::Stop() { | ||
| 588 | complete_event.Set(); | ||
| 589 | } | ||
| 590 | |||
| 591 | } // namespace InputCommon::CemuhookUDP | ||
diff --git a/src/input_common/udp/client.h b/src/input_common/drivers/udp_client.h index 380f9bb76..1adc947c4 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/drivers/udp_client.h | |||
| @@ -4,20 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <mutex> | ||
| 10 | #include <optional> | 7 | #include <optional> |
| 11 | #include <string> | 8 | |
| 12 | #include <thread> | ||
| 13 | #include <tuple> | ||
| 14 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 15 | #include "common/param_package.h" | ||
| 16 | #include "common/thread.h" | 10 | #include "common/thread.h" |
| 17 | #include "common/threadsafe_queue.h" | 11 | #include "input_common/input_engine.h" |
| 18 | #include "common/vector_math.h" | ||
| 19 | #include "core/frontend/input.h" | ||
| 20 | #include "input_common/motion_input.h" | ||
| 21 | 12 | ||
| 22 | namespace InputCommon::CemuhookUDP { | 13 | namespace InputCommon::CemuhookUDP { |
| 23 | 14 | ||
| @@ -30,16 +21,6 @@ struct TouchPad; | |||
| 30 | struct Version; | 21 | struct Version; |
| 31 | } // namespace Response | 22 | } // namespace Response |
| 32 | 23 | ||
| 33 | enum class PadMotion { | ||
| 34 | GyroX, | ||
| 35 | GyroY, | ||
| 36 | GyroZ, | ||
| 37 | AccX, | ||
| 38 | AccY, | ||
| 39 | AccZ, | ||
| 40 | Undefined, | ||
| 41 | }; | ||
| 42 | |||
| 43 | enum class PadTouch { | 24 | enum class PadTouch { |
| 44 | Click, | 25 | Click, |
| 45 | Undefined, | 26 | Undefined, |
| @@ -49,14 +30,10 @@ struct UDPPadStatus { | |||
| 49 | std::string host{"127.0.0.1"}; | 30 | std::string host{"127.0.0.1"}; |
| 50 | u16 port{26760}; | 31 | u16 port{26760}; |
| 51 | std::size_t pad_index{}; | 32 | std::size_t pad_index{}; |
| 52 | PadMotion motion{PadMotion::Undefined}; | ||
| 53 | f32 motion_value{0.0f}; | ||
| 54 | }; | 33 | }; |
| 55 | 34 | ||
| 56 | struct DeviceStatus { | 35 | struct DeviceStatus { |
| 57 | std::mutex update_mutex; | 36 | std::mutex update_mutex; |
| 58 | Input::MotionStatus motion_status; | ||
| 59 | std::tuple<float, float, bool> touch_status; | ||
| 60 | 37 | ||
| 61 | // calibration data for scaling the device's touch area to 3ds | 38 | // calibration data for scaling the device's touch area to 3ds |
| 62 | struct CalibrationData { | 39 | struct CalibrationData { |
| @@ -68,48 +45,85 @@ struct DeviceStatus { | |||
| 68 | std::optional<CalibrationData> touch_calibration; | 45 | std::optional<CalibrationData> touch_calibration; |
| 69 | }; | 46 | }; |
| 70 | 47 | ||
| 71 | class Client { | 48 | /** |
| 49 | * A button device factory representing a keyboard. It receives keyboard events and forward them | ||
| 50 | * to all button devices it created. | ||
| 51 | */ | ||
| 52 | class UDPClient final : public InputEngine { | ||
| 72 | public: | 53 | public: |
| 73 | // Initialize the UDP client capture and read sequence | 54 | explicit UDPClient(std::string input_engine_); |
| 74 | Client(); | 55 | ~UDPClient() override; |
| 75 | |||
| 76 | // Close and release the client | ||
| 77 | ~Client(); | ||
| 78 | |||
| 79 | // Used for polling | ||
| 80 | void BeginConfiguration(); | ||
| 81 | void EndConfiguration(); | ||
| 82 | |||
| 83 | std::vector<Common::ParamPackage> GetInputDevices() const; | ||
| 84 | 56 | ||
| 85 | bool DeviceConnected(std::size_t pad) const; | ||
| 86 | void ReloadSockets(); | 57 | void ReloadSockets(); |
| 87 | 58 | ||
| 88 | Common::SPSCQueue<UDPPadStatus>& GetPadQueue(); | 59 | /// Used for automapping features |
| 89 | const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const; | 60 | std::vector<Common::ParamPackage> GetInputDevices() const override; |
| 61 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; | ||
| 62 | AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; | ||
| 63 | MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override; | ||
| 64 | Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; | ||
| 90 | 65 | ||
| 91 | DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad); | 66 | private: |
| 92 | const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const; | 67 | enum class PadButton { |
| 68 | Undefined = 0x0000, | ||
| 69 | Share = 0x0001, | ||
| 70 | L3 = 0x0002, | ||
| 71 | R3 = 0x0004, | ||
| 72 | Options = 0x0008, | ||
| 73 | Up = 0x0010, | ||
| 74 | Right = 0x0020, | ||
| 75 | Down = 0x0040, | ||
| 76 | Left = 0x0080, | ||
| 77 | L2 = 0x0100, | ||
| 78 | R2 = 0x0200, | ||
| 79 | L1 = 0x0400, | ||
| 80 | R1 = 0x0800, | ||
| 81 | Triangle = 0x1000, | ||
| 82 | Circle = 0x2000, | ||
| 83 | Cross = 0x4000, | ||
| 84 | Square = 0x8000, | ||
| 85 | Touch1 = 0x10000, | ||
| 86 | touch2 = 0x20000, | ||
| 87 | }; | ||
| 93 | 88 | ||
| 94 | Input::TouchStatus& GetTouchState(); | 89 | enum class PadAxes : u8 { |
| 95 | const Input::TouchStatus& GetTouchState() const; | 90 | LeftStickX, |
| 91 | LeftStickY, | ||
| 92 | RightStickX, | ||
| 93 | RightStickY, | ||
| 94 | AnalogLeft, | ||
| 95 | AnalogDown, | ||
| 96 | AnalogRight, | ||
| 97 | AnalogUp, | ||
| 98 | AnalogSquare, | ||
| 99 | AnalogCross, | ||
| 100 | AnalogCircle, | ||
| 101 | AnalogTriangle, | ||
| 102 | AnalogR1, | ||
| 103 | AnalogL1, | ||
| 104 | AnalogR2, | ||
| 105 | AnalogL3, | ||
| 106 | AnalogR3, | ||
| 107 | Touch1X, | ||
| 108 | Touch1Y, | ||
| 109 | Touch2X, | ||
| 110 | Touch2Y, | ||
| 111 | Undefined, | ||
| 112 | }; | ||
| 96 | 113 | ||
| 97 | private: | ||
| 98 | struct PadData { | 114 | struct PadData { |
| 99 | std::size_t pad_index{}; | 115 | std::size_t pad_index{}; |
| 100 | bool connected{}; | 116 | bool connected{}; |
| 101 | DeviceStatus status; | 117 | DeviceStatus status; |
| 102 | u64 packet_sequence{}; | 118 | u64 packet_sequence{}; |
| 103 | 119 | ||
| 104 | // Realtime values | ||
| 105 | // motion is initalized with PID values for drift correction on joycons | ||
| 106 | InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f}; | ||
| 107 | std::chrono::time_point<std::chrono::steady_clock> last_update; | 120 | std::chrono::time_point<std::chrono::steady_clock> last_update; |
| 108 | }; | 121 | }; |
| 109 | 122 | ||
| 110 | struct ClientConnection { | 123 | struct ClientConnection { |
| 111 | ClientConnection(); | 124 | ClientConnection(); |
| 112 | ~ClientConnection(); | 125 | ~ClientConnection(); |
| 126 | Common::UUID uuid{"7F000001"}; | ||
| 113 | std::string host{"127.0.0.1"}; | 127 | std::string host{"127.0.0.1"}; |
| 114 | u16 port{26760}; | 128 | u16 port{26760}; |
| 115 | s8 active{-1}; | 129 | s8 active{-1}; |
| @@ -127,28 +141,16 @@ private: | |||
| 127 | void OnPortInfo(Response::PortInfo); | 141 | void OnPortInfo(Response::PortInfo); |
| 128 | void OnPadData(Response::PadData, std::size_t client); | 142 | void OnPadData(Response::PadData, std::size_t client); |
| 129 | void StartCommunication(std::size_t client, const std::string& host, u16 port); | 143 | void StartCommunication(std::size_t client, const std::string& host, u16 port); |
| 130 | void UpdateYuzuSettings(std::size_t client, std::size_t pad_index, | 144 | const PadIdentifier GetPadIdentifier(std::size_t pad_index) const; |
| 131 | const Common::Vec3<float>& acc, const Common::Vec3<float>& gyro); | 145 | const Common::UUID GetHostUUID(const std::string host) const; |
| 132 | |||
| 133 | // Returns an unused finger id, if there is no fingers available std::nullopt will be | ||
| 134 | // returned | ||
| 135 | std::optional<std::size_t> GetUnusedFingerID() const; | ||
| 136 | |||
| 137 | // Merges and updates all touch inputs into the touch_status array | ||
| 138 | void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id); | ||
| 139 | 146 | ||
| 140 | bool configuring = false; | 147 | Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; |
| 141 | 148 | ||
| 142 | // Allocate clients for 8 udp servers | 149 | // Allocate clients for 8 udp servers |
| 143 | static constexpr std::size_t MAX_UDP_CLIENTS = 8; | 150 | static constexpr std::size_t MAX_UDP_CLIENTS = 8; |
| 144 | static constexpr std::size_t PADS_PER_CLIENT = 4; | 151 | static constexpr std::size_t PADS_PER_CLIENT = 4; |
| 145 | // Each client can have up 2 touch inputs | ||
| 146 | static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2; | ||
| 147 | std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{}; | 152 | std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{}; |
| 148 | std::array<ClientConnection, MAX_UDP_CLIENTS> clients{}; | 153 | std::array<ClientConnection, MAX_UDP_CLIENTS> clients{}; |
| 149 | Common::SPSCQueue<UDPPadStatus> pad_queue{}; | ||
| 150 | Input::TouchStatus touch_status{}; | ||
| 151 | std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{}; | ||
| 152 | }; | 154 | }; |
| 153 | 155 | ||
| 154 | /// An async job allowing configuration of the touchpad calibration. | 156 | /// An async job allowing configuration of the touchpad calibration. |
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h deleted file mode 100644 index e5de5e94f..000000000 --- a/src/input_common/gcadapter/gc_adapter.h +++ /dev/null | |||
| @@ -1,168 +0,0 @@ | |||
| 1 | // Copyright 2014 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | #include <algorithm> | ||
| 7 | #include <functional> | ||
| 8 | #include <mutex> | ||
| 9 | #include <thread> | ||
| 10 | #include <unordered_map> | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/threadsafe_queue.h" | ||
| 13 | #include "input_common/main.h" | ||
| 14 | |||
| 15 | struct libusb_context; | ||
| 16 | struct libusb_device; | ||
| 17 | struct libusb_device_handle; | ||
| 18 | |||
| 19 | namespace GCAdapter { | ||
| 20 | |||
| 21 | enum class PadButton { | ||
| 22 | Undefined = 0x0000, | ||
| 23 | ButtonLeft = 0x0001, | ||
| 24 | ButtonRight = 0x0002, | ||
| 25 | ButtonDown = 0x0004, | ||
| 26 | ButtonUp = 0x0008, | ||
| 27 | TriggerZ = 0x0010, | ||
| 28 | TriggerR = 0x0020, | ||
| 29 | TriggerL = 0x0040, | ||
| 30 | ButtonA = 0x0100, | ||
| 31 | ButtonB = 0x0200, | ||
| 32 | ButtonX = 0x0400, | ||
| 33 | ButtonY = 0x0800, | ||
| 34 | ButtonStart = 0x1000, | ||
| 35 | // Below is for compatibility with "AxisButton" type | ||
| 36 | Stick = 0x2000, | ||
| 37 | }; | ||
| 38 | |||
| 39 | enum class PadAxes : u8 { | ||
| 40 | StickX, | ||
| 41 | StickY, | ||
| 42 | SubstickX, | ||
| 43 | SubstickY, | ||
| 44 | TriggerLeft, | ||
| 45 | TriggerRight, | ||
| 46 | Undefined, | ||
| 47 | }; | ||
| 48 | |||
| 49 | enum class ControllerTypes { | ||
| 50 | None, | ||
| 51 | Wired, | ||
| 52 | Wireless, | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct GCPadStatus { | ||
| 56 | std::size_t port{}; | ||
| 57 | |||
| 58 | PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits | ||
| 59 | |||
| 60 | PadAxes axis{PadAxes::Undefined}; | ||
| 61 | s16 axis_value{}; | ||
| 62 | u8 axis_threshold{50}; | ||
| 63 | }; | ||
| 64 | |||
| 65 | struct GCController { | ||
| 66 | ControllerTypes type{}; | ||
| 67 | bool enable_vibration{}; | ||
| 68 | u8 rumble_amplitude{}; | ||
| 69 | u16 buttons{}; | ||
| 70 | PadButton last_button{}; | ||
| 71 | std::array<s16, 6> axis_values{}; | ||
| 72 | std::array<u8, 6> axis_origin{}; | ||
| 73 | u8 reset_origin_counter{}; | ||
| 74 | }; | ||
| 75 | |||
| 76 | class Adapter { | ||
| 77 | public: | ||
| 78 | Adapter(); | ||
| 79 | ~Adapter(); | ||
| 80 | |||
| 81 | /// Request a vibration for a controller | ||
| 82 | bool RumblePlay(std::size_t port, u8 amplitude); | ||
| 83 | |||
| 84 | /// Used for polling | ||
| 85 | void BeginConfiguration(); | ||
| 86 | void EndConfiguration(); | ||
| 87 | |||
| 88 | Common::SPSCQueue<GCPadStatus>& GetPadQueue(); | ||
| 89 | const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const; | ||
| 90 | |||
| 91 | GCController& GetPadState(std::size_t port); | ||
| 92 | const GCController& GetPadState(std::size_t port) const; | ||
| 93 | |||
| 94 | /// Returns true if there is a device connected to port | ||
| 95 | bool DeviceConnected(std::size_t port) const; | ||
| 96 | |||
| 97 | /// Used for automapping features | ||
| 98 | std::vector<Common::ParamPackage> GetInputDevices() const; | ||
| 99 | InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; | ||
| 100 | InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; | ||
| 101 | |||
| 102 | private: | ||
| 103 | using AdapterPayload = std::array<u8, 37>; | ||
| 104 | |||
| 105 | void UpdatePadType(std::size_t port, ControllerTypes pad_type); | ||
| 106 | void UpdateControllers(const AdapterPayload& adapter_payload); | ||
| 107 | void UpdateYuzuSettings(std::size_t port); | ||
| 108 | void UpdateStateButtons(std::size_t port, u8 b1, u8 b2); | ||
| 109 | void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload); | ||
| 110 | void UpdateVibrations(); | ||
| 111 | |||
| 112 | void AdapterInputThread(); | ||
| 113 | |||
| 114 | void AdapterScanThread(); | ||
| 115 | |||
| 116 | bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size); | ||
| 117 | |||
| 118 | // Updates vibration state of all controllers | ||
| 119 | void SendVibrations(); | ||
| 120 | |||
| 121 | /// For use in initialization, querying devices to find the adapter | ||
| 122 | void Setup(); | ||
| 123 | |||
| 124 | /// Resets status of all GC controller devices to a disconnected state | ||
| 125 | void ResetDevices(); | ||
| 126 | |||
| 127 | /// Resets status of device connected to a disconnected state | ||
| 128 | void ResetDevice(std::size_t port); | ||
| 129 | |||
| 130 | /// Returns true if we successfully gain access to GC Adapter | ||
| 131 | bool CheckDeviceAccess(); | ||
| 132 | |||
| 133 | /// Captures GC Adapter endpoint address | ||
| 134 | /// Returns true if the endpoint was set correctly | ||
| 135 | bool GetGCEndpoint(libusb_device* device); | ||
| 136 | |||
| 137 | /// For shutting down, clear all data, join all threads, release usb | ||
| 138 | void Reset(); | ||
| 139 | |||
| 140 | // Join all threads | ||
| 141 | void JoinThreads(); | ||
| 142 | |||
| 143 | // Release usb handles | ||
| 144 | void ClearLibusbHandle(); | ||
| 145 | |||
| 146 | libusb_device_handle* usb_adapter_handle = nullptr; | ||
| 147 | std::array<GCController, 4> pads; | ||
| 148 | Common::SPSCQueue<GCPadStatus> pad_queue; | ||
| 149 | |||
| 150 | std::thread adapter_input_thread; | ||
| 151 | std::thread adapter_scan_thread; | ||
| 152 | bool adapter_input_thread_running; | ||
| 153 | bool adapter_scan_thread_running; | ||
| 154 | bool restart_scan_thread; | ||
| 155 | |||
| 156 | libusb_context* libusb_ctx; | ||
| 157 | |||
| 158 | u8 input_endpoint{0}; | ||
| 159 | u8 output_endpoint{0}; | ||
| 160 | u8 input_error_counter{0}; | ||
| 161 | u8 output_error_counter{0}; | ||
| 162 | int vibration_counter{0}; | ||
| 163 | |||
| 164 | bool configuring{false}; | ||
| 165 | bool rumble_enabled{true}; | ||
| 166 | bool vibration_changed{true}; | ||
| 167 | }; | ||
| 168 | } // namespace GCAdapter | ||
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp deleted file mode 100644 index 1b6ded8d6..000000000 --- a/src/input_common/gcadapter/gc_poller.cpp +++ /dev/null | |||
| @@ -1,356 +0,0 @@ | |||
| 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 <atomic> | ||
| 6 | #include <list> | ||
| 7 | #include <mutex> | ||
| 8 | #include <utility> | ||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/threadsafe_queue.h" | ||
| 11 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 12 | #include "input_common/gcadapter/gc_poller.h" | ||
| 13 | |||
| 14 | namespace InputCommon { | ||
| 15 | |||
| 16 | class GCButton final : public Input::ButtonDevice { | ||
| 17 | public: | ||
| 18 | explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter) | ||
| 19 | : port(port_), button(button_), gcadapter(adapter) {} | ||
| 20 | |||
| 21 | ~GCButton() override; | ||
| 22 | |||
| 23 | bool GetStatus() const override { | ||
| 24 | if (gcadapter->DeviceConnected(port)) { | ||
| 25 | return (gcadapter->GetPadState(port).buttons & button) != 0; | ||
| 26 | } | ||
| 27 | return false; | ||
| 28 | } | ||
| 29 | |||
| 30 | private: | ||
| 31 | const u32 port; | ||
| 32 | const s32 button; | ||
| 33 | const GCAdapter::Adapter* gcadapter; | ||
| 34 | }; | ||
| 35 | |||
| 36 | class GCAxisButton final : public Input::ButtonDevice { | ||
| 37 | public: | ||
| 38 | explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_, | ||
| 39 | const GCAdapter::Adapter* adapter) | ||
| 40 | : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), | ||
| 41 | gcadapter(adapter) {} | ||
| 42 | |||
| 43 | bool GetStatus() const override { | ||
| 44 | if (gcadapter->DeviceConnected(port)) { | ||
| 45 | const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis); | ||
| 46 | const float axis_value = current_axis_value / 128.0f; | ||
| 47 | if (trigger_if_greater) { | ||
| 48 | // TODO: Might be worthwile to set a slider for the trigger threshold. It is | ||
| 49 | // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick | ||
| 50 | return axis_value > threshold; | ||
| 51 | } | ||
| 52 | return axis_value < -threshold; | ||
| 53 | } | ||
| 54 | return false; | ||
| 55 | } | ||
| 56 | |||
| 57 | private: | ||
| 58 | const u32 port; | ||
| 59 | const u32 axis; | ||
| 60 | float threshold; | ||
| 61 | bool trigger_if_greater; | ||
| 62 | const GCAdapter::Adapter* gcadapter; | ||
| 63 | }; | ||
| 64 | |||
| 65 | GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | ||
| 66 | : adapter(std::move(adapter_)) {} | ||
| 67 | |||
| 68 | GCButton::~GCButton() = default; | ||
| 69 | |||
| 70 | std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) { | ||
| 71 | const auto button_id = params.Get("button", 0); | ||
| 72 | const auto port = static_cast<u32>(params.Get("port", 0)); | ||
| 73 | |||
| 74 | constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick); | ||
| 75 | |||
| 76 | // button is not an axis/stick button | ||
| 77 | if (button_id != PAD_STICK_ID) { | ||
| 78 | return std::make_unique<GCButton>(port, button_id, adapter.get()); | ||
| 79 | } | ||
| 80 | |||
| 81 | // For Axis buttons, used by the binary sticks. | ||
| 82 | if (button_id == PAD_STICK_ID) { | ||
| 83 | const int axis = params.Get("axis", 0); | ||
| 84 | const float threshold = params.Get("threshold", 0.25f); | ||
| 85 | const std::string direction_name = params.Get("direction", ""); | ||
| 86 | bool trigger_if_greater; | ||
| 87 | if (direction_name == "+") { | ||
| 88 | trigger_if_greater = true; | ||
| 89 | } else if (direction_name == "-") { | ||
| 90 | trigger_if_greater = false; | ||
| 91 | } else { | ||
| 92 | trigger_if_greater = true; | ||
| 93 | LOG_ERROR(Input, "Unknown direction {}", direction_name); | ||
| 94 | } | ||
| 95 | return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater, | ||
| 96 | adapter.get()); | ||
| 97 | } | ||
| 98 | |||
| 99 | return nullptr; | ||
| 100 | } | ||
| 101 | |||
| 102 | Common::ParamPackage GCButtonFactory::GetNextInput() const { | ||
| 103 | Common::ParamPackage params; | ||
| 104 | GCAdapter::GCPadStatus pad; | ||
| 105 | auto& queue = adapter->GetPadQueue(); | ||
| 106 | while (queue.Pop(pad)) { | ||
| 107 | // This while loop will break on the earliest detected button | ||
| 108 | params.Set("engine", "gcpad"); | ||
| 109 | params.Set("port", static_cast<s32>(pad.port)); | ||
| 110 | if (pad.button != GCAdapter::PadButton::Undefined) { | ||
| 111 | params.Set("button", static_cast<u16>(pad.button)); | ||
| 112 | } | ||
| 113 | |||
| 114 | // For Axis button implementation | ||
| 115 | if (pad.axis != GCAdapter::PadAxes::Undefined) { | ||
| 116 | params.Set("axis", static_cast<u8>(pad.axis)); | ||
| 117 | params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick)); | ||
| 118 | params.Set("threshold", "0.25"); | ||
| 119 | if (pad.axis_value > 0) { | ||
| 120 | params.Set("direction", "+"); | ||
| 121 | } else { | ||
| 122 | params.Set("direction", "-"); | ||
| 123 | } | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | return params; | ||
| 128 | } | ||
| 129 | |||
| 130 | void GCButtonFactory::BeginConfiguration() { | ||
| 131 | polling = true; | ||
| 132 | adapter->BeginConfiguration(); | ||
| 133 | } | ||
| 134 | |||
| 135 | void GCButtonFactory::EndConfiguration() { | ||
| 136 | polling = false; | ||
| 137 | adapter->EndConfiguration(); | ||
| 138 | } | ||
| 139 | |||
| 140 | class GCAnalog final : public Input::AnalogDevice { | ||
| 141 | public: | ||
| 142 | explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_, | ||
| 143 | float deadzone_, float range_, const GCAdapter::Adapter* adapter) | ||
| 144 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_), | ||
| 145 | deadzone(deadzone_), range(range_), gcadapter(adapter) {} | ||
| 146 | |||
| 147 | float GetAxis(u32 axis) const { | ||
| 148 | if (gcadapter->DeviceConnected(port)) { | ||
| 149 | std::lock_guard lock{mutex}; | ||
| 150 | const auto axis_value = | ||
| 151 | static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis)); | ||
| 152 | return (axis_value) / (100.0f * range); | ||
| 153 | } | ||
| 154 | return 0.0f; | ||
| 155 | } | ||
| 156 | |||
| 157 | std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { | ||
| 158 | float x = GetAxis(analog_axis_x); | ||
| 159 | float y = GetAxis(analog_axis_y); | ||
| 160 | if (invert_x) { | ||
| 161 | x = -x; | ||
| 162 | } | ||
| 163 | if (invert_y) { | ||
| 164 | y = -y; | ||
| 165 | } | ||
| 166 | // Make sure the coordinates are in the unit circle, | ||
| 167 | // otherwise normalize it. | ||
| 168 | float r = x * x + y * y; | ||
| 169 | if (r > 1.0f) { | ||
| 170 | r = std::sqrt(r); | ||
| 171 | x /= r; | ||
| 172 | y /= r; | ||
| 173 | } | ||
| 174 | |||
| 175 | return {x, y}; | ||
| 176 | } | ||
| 177 | |||
| 178 | std::tuple<float, float> GetStatus() const override { | ||
| 179 | const auto [x, y] = GetAnalog(axis_x, axis_y); | ||
| 180 | const float r = std::sqrt((x * x) + (y * y)); | ||
| 181 | if (r > deadzone) { | ||
| 182 | return {x / r * (r - deadzone) / (1 - deadzone), | ||
| 183 | y / r * (r - deadzone) / (1 - deadzone)}; | ||
| 184 | } | ||
| 185 | return {0.0f, 0.0f}; | ||
| 186 | } | ||
| 187 | |||
| 188 | std::tuple<float, float> GetRawStatus() const override { | ||
| 189 | const float x = GetAxis(axis_x); | ||
| 190 | const float y = GetAxis(axis_y); | ||
| 191 | return {x, y}; | ||
| 192 | } | ||
| 193 | |||
| 194 | Input::AnalogProperties GetAnalogProperties() const override { | ||
| 195 | return {deadzone, range, 0.5f}; | ||
| 196 | } | ||
| 197 | |||
| 198 | bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { | ||
| 199 | const auto [x, y] = GetStatus(); | ||
| 200 | const float directional_deadzone = 0.5f; | ||
| 201 | switch (direction) { | ||
| 202 | case Input::AnalogDirection::RIGHT: | ||
| 203 | return x > directional_deadzone; | ||
| 204 | case Input::AnalogDirection::LEFT: | ||
| 205 | return x < -directional_deadzone; | ||
| 206 | case Input::AnalogDirection::UP: | ||
| 207 | return y > directional_deadzone; | ||
| 208 | case Input::AnalogDirection::DOWN: | ||
| 209 | return y < -directional_deadzone; | ||
| 210 | } | ||
| 211 | return false; | ||
| 212 | } | ||
| 213 | |||
| 214 | private: | ||
| 215 | const u32 port; | ||
| 216 | const u32 axis_x; | ||
| 217 | const u32 axis_y; | ||
| 218 | const bool invert_x; | ||
| 219 | const bool invert_y; | ||
| 220 | const float deadzone; | ||
| 221 | const float range; | ||
| 222 | const GCAdapter::Adapter* gcadapter; | ||
| 223 | mutable std::mutex mutex; | ||
| 224 | }; | ||
| 225 | |||
| 226 | /// An analog device factory that creates analog devices from GC Adapter | ||
| 227 | GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | ||
| 228 | : adapter(std::move(adapter_)) {} | ||
| 229 | |||
| 230 | /** | ||
| 231 | * Creates analog device from joystick axes | ||
| 232 | * @param params contains parameters for creating the device: | ||
| 233 | * - "port": the nth gcpad on the adapter | ||
| 234 | * - "axis_x": the index of the axis to be bind as x-axis | ||
| 235 | * - "axis_y": the index of the axis to be bind as y-axis | ||
| 236 | */ | ||
| 237 | std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) { | ||
| 238 | const auto port = static_cast<u32>(params.Get("port", 0)); | ||
| 239 | const auto axis_x = static_cast<u32>(params.Get("axis_x", 0)); | ||
| 240 | const auto axis_y = static_cast<u32>(params.Get("axis_y", 1)); | ||
| 241 | const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); | ||
| 242 | const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); | ||
| 243 | const std::string invert_x_value = params.Get("invert_x", "+"); | ||
| 244 | const std::string invert_y_value = params.Get("invert_y", "+"); | ||
| 245 | const bool invert_x = invert_x_value == "-"; | ||
| 246 | const bool invert_y = invert_y_value == "-"; | ||
| 247 | |||
| 248 | return std::make_unique<GCAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range, | ||
| 249 | adapter.get()); | ||
| 250 | } | ||
| 251 | |||
| 252 | void GCAnalogFactory::BeginConfiguration() { | ||
| 253 | polling = true; | ||
| 254 | adapter->BeginConfiguration(); | ||
| 255 | } | ||
| 256 | |||
| 257 | void GCAnalogFactory::EndConfiguration() { | ||
| 258 | polling = false; | ||
| 259 | adapter->EndConfiguration(); | ||
| 260 | } | ||
| 261 | |||
| 262 | Common::ParamPackage GCAnalogFactory::GetNextInput() { | ||
| 263 | GCAdapter::GCPadStatus pad; | ||
| 264 | Common::ParamPackage params; | ||
| 265 | auto& queue = adapter->GetPadQueue(); | ||
| 266 | while (queue.Pop(pad)) { | ||
| 267 | if (pad.button != GCAdapter::PadButton::Undefined) { | ||
| 268 | params.Set("engine", "gcpad"); | ||
| 269 | params.Set("port", static_cast<s32>(pad.port)); | ||
| 270 | params.Set("button", static_cast<u16>(pad.button)); | ||
| 271 | return params; | ||
| 272 | } | ||
| 273 | if (pad.axis == GCAdapter::PadAxes::Undefined || | ||
| 274 | std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) { | ||
| 275 | continue; | ||
| 276 | } | ||
| 277 | // An analog device needs two axes, so we need to store the axis for later and wait for | ||
| 278 | // a second input event. The axes also must be from the same joystick. | ||
| 279 | const u8 axis = static_cast<u8>(pad.axis); | ||
| 280 | if (axis == 0 || axis == 1) { | ||
| 281 | analog_x_axis = 0; | ||
| 282 | analog_y_axis = 1; | ||
| 283 | controller_number = static_cast<s32>(pad.port); | ||
| 284 | break; | ||
| 285 | } | ||
| 286 | if (axis == 2 || axis == 3) { | ||
| 287 | analog_x_axis = 2; | ||
| 288 | analog_y_axis = 3; | ||
| 289 | controller_number = static_cast<s32>(pad.port); | ||
| 290 | break; | ||
| 291 | } | ||
| 292 | |||
| 293 | if (analog_x_axis == -1) { | ||
| 294 | analog_x_axis = axis; | ||
| 295 | controller_number = static_cast<s32>(pad.port); | ||
| 296 | } else if (analog_y_axis == -1 && analog_x_axis != axis && | ||
| 297 | controller_number == static_cast<s32>(pad.port)) { | ||
| 298 | analog_y_axis = axis; | ||
| 299 | break; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | if (analog_x_axis != -1 && analog_y_axis != -1) { | ||
| 303 | params.Set("engine", "gcpad"); | ||
| 304 | params.Set("port", controller_number); | ||
| 305 | params.Set("axis_x", analog_x_axis); | ||
| 306 | params.Set("axis_y", analog_y_axis); | ||
| 307 | params.Set("invert_x", "+"); | ||
| 308 | params.Set("invert_y", "+"); | ||
| 309 | analog_x_axis = -1; | ||
| 310 | analog_y_axis = -1; | ||
| 311 | controller_number = -1; | ||
| 312 | return params; | ||
| 313 | } | ||
| 314 | return params; | ||
| 315 | } | ||
| 316 | |||
| 317 | class GCVibration final : public Input::VibrationDevice { | ||
| 318 | public: | ||
| 319 | explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter) | ||
| 320 | : port(port_), gcadapter(adapter) {} | ||
| 321 | |||
| 322 | u8 GetStatus() const override { | ||
| 323 | return gcadapter->RumblePlay(port, 0); | ||
| 324 | } | ||
| 325 | |||
| 326 | bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high, | ||
| 327 | [[maybe_unused]] f32 freq_high) const override { | ||
| 328 | const auto mean_amplitude = (amp_low + amp_high) * 0.5f; | ||
| 329 | const auto processed_amplitude = | ||
| 330 | static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8); | ||
| 331 | |||
| 332 | return gcadapter->RumblePlay(port, processed_amplitude); | ||
| 333 | } | ||
| 334 | |||
| 335 | private: | ||
| 336 | const u32 port; | ||
| 337 | GCAdapter::Adapter* gcadapter; | ||
| 338 | }; | ||
| 339 | |||
| 340 | /// An vibration device factory that creates vibration devices from GC Adapter | ||
| 341 | GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | ||
| 342 | : adapter(std::move(adapter_)) {} | ||
| 343 | |||
| 344 | /** | ||
| 345 | * Creates a vibration device from a joystick | ||
| 346 | * @param params contains parameters for creating the device: | ||
| 347 | * - "port": the nth gcpad on the adapter | ||
| 348 | */ | ||
| 349 | std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create( | ||
| 350 | const Common::ParamPackage& params) { | ||
| 351 | const auto port = static_cast<u32>(params.Get("port", 0)); | ||
| 352 | |||
| 353 | return std::make_unique<GCVibration>(port, adapter.get()); | ||
| 354 | } | ||
| 355 | |||
| 356 | } // namespace InputCommon | ||
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h deleted file mode 100644 index d1271e3ea..000000000 --- a/src/input_common/gcadapter/gc_poller.h +++ /dev/null | |||
| @@ -1,78 +0,0 @@ | |||
| 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 <memory> | ||
| 8 | #include "core/frontend/input.h" | ||
| 9 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * A button device factory representing a gcpad. It receives gcpad events and forward them | ||
| 15 | * to all button devices it created. | ||
| 16 | */ | ||
| 17 | class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> { | ||
| 18 | public: | ||
| 19 | explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Creates a button device from a button press | ||
| 23 | * @param params contains parameters for creating the device: | ||
| 24 | * - "code": the code of the key to bind with the button | ||
| 25 | */ | ||
| 26 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; | ||
| 27 | |||
| 28 | Common::ParamPackage GetNextInput() const; | ||
| 29 | |||
| 30 | /// For device input configuration/polling | ||
| 31 | void BeginConfiguration(); | ||
| 32 | void EndConfiguration(); | ||
| 33 | |||
| 34 | bool IsPolling() const { | ||
| 35 | return polling; | ||
| 36 | } | ||
| 37 | |||
| 38 | private: | ||
| 39 | std::shared_ptr<GCAdapter::Adapter> adapter; | ||
| 40 | bool polling = false; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /// An analog device factory that creates analog devices from GC Adapter | ||
| 44 | class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> { | ||
| 45 | public: | ||
| 46 | explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); | ||
| 47 | |||
| 48 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; | ||
| 49 | Common::ParamPackage GetNextInput(); | ||
| 50 | |||
| 51 | /// For device input configuration/polling | ||
| 52 | void BeginConfiguration(); | ||
| 53 | void EndConfiguration(); | ||
| 54 | |||
| 55 | bool IsPolling() const { | ||
| 56 | return polling; | ||
| 57 | } | ||
| 58 | |||
| 59 | private: | ||
| 60 | std::shared_ptr<GCAdapter::Adapter> adapter; | ||
| 61 | int analog_x_axis = -1; | ||
| 62 | int analog_y_axis = -1; | ||
| 63 | int controller_number = -1; | ||
| 64 | bool polling = false; | ||
| 65 | }; | ||
| 66 | |||
| 67 | /// A vibration device factory creates vibration devices from GC Adapter | ||
| 68 | class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> { | ||
| 69 | public: | ||
| 70 | explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); | ||
| 71 | |||
| 72 | std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override; | ||
| 73 | |||
| 74 | private: | ||
| 75 | std::shared_ptr<GCAdapter::Adapter> adapter; | ||
| 76 | }; | ||
| 77 | |||
| 78 | } // namespace InputCommon | ||
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp new file mode 100644 index 000000000..e23394f5f --- /dev/null +++ b/src/input_common/helpers/stick_from_buttons.cpp | |||
| @@ -0,0 +1,317 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <chrono> | ||
| 6 | #include <cmath> | ||
| 7 | #include "common/math_util.h" | ||
| 8 | #include "common/settings.h" | ||
| 9 | #include "input_common/helpers/stick_from_buttons.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | class Stick final : public Common::Input::InputDevice { | ||
| 14 | public: | ||
| 15 | using Button = std::unique_ptr<Common::Input::InputDevice>; | ||
| 16 | |||
| 17 | Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, | ||
| 18 | float modifier_scale_, float modifier_angle_) | ||
| 19 | : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), | ||
| 20 | right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), | ||
| 21 | modifier_angle(modifier_angle_) { | ||
| 22 | up->SetCallback({ | ||
| 23 | .on_change = | ||
| 24 | [this](const Common::Input::CallbackStatus& callback_) { | ||
| 25 | UpdateUpButtonStatus(callback_); | ||
| 26 | }, | ||
| 27 | }); | ||
| 28 | down->SetCallback({ | ||
| 29 | .on_change = | ||
| 30 | [this](const Common::Input::CallbackStatus& callback_) { | ||
| 31 | UpdateDownButtonStatus(callback_); | ||
| 32 | }, | ||
| 33 | }); | ||
| 34 | left->SetCallback({ | ||
| 35 | .on_change = | ||
| 36 | [this](const Common::Input::CallbackStatus& callback_) { | ||
| 37 | UpdateLeftButtonStatus(callback_); | ||
| 38 | }, | ||
| 39 | }); | ||
| 40 | right->SetCallback({ | ||
| 41 | .on_change = | ||
| 42 | [this](const Common::Input::CallbackStatus& callback_) { | ||
| 43 | UpdateRightButtonStatus(callback_); | ||
| 44 | }, | ||
| 45 | }); | ||
| 46 | modifier->SetCallback({ | ||
| 47 | .on_change = | ||
| 48 | [this](const Common::Input::CallbackStatus& callback_) { | ||
| 49 | UpdateModButtonStatus(callback_); | ||
| 50 | }, | ||
| 51 | }); | ||
| 52 | last_x_axis_value = 0.0f; | ||
| 53 | last_y_axis_value = 0.0f; | ||
| 54 | } | ||
| 55 | |||
| 56 | bool IsAngleGreater(float old_angle, float new_angle) const { | ||
| 57 | constexpr float TAU = Common::PI * 2.0f; | ||
| 58 | // Use wider angle to ease the transition. | ||
| 59 | constexpr float aperture = TAU * 0.15f; | ||
| 60 | const float top_limit = new_angle + aperture; | ||
| 61 | return (old_angle > new_angle && old_angle <= top_limit) || | ||
| 62 | (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); | ||
| 63 | } | ||
| 64 | |||
| 65 | bool IsAngleSmaller(float old_angle, float new_angle) const { | ||
| 66 | constexpr float TAU = Common::PI * 2.0f; | ||
| 67 | // Use wider angle to ease the transition. | ||
| 68 | constexpr float aperture = TAU * 0.15f; | ||
| 69 | const float bottom_limit = new_angle - aperture; | ||
| 70 | return (old_angle >= bottom_limit && old_angle < new_angle) || | ||
| 71 | (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); | ||
| 72 | } | ||
| 73 | |||
| 74 | float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { | ||
| 75 | constexpr float TAU = Common::PI * 2.0f; | ||
| 76 | float new_angle = angle; | ||
| 77 | |||
| 78 | auto time_difference = static_cast<float>( | ||
| 79 | std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); | ||
| 80 | time_difference /= 1000.0f * 1000.0f; | ||
| 81 | if (time_difference > 0.5f) { | ||
| 82 | time_difference = 0.5f; | ||
| 83 | } | ||
| 84 | |||
| 85 | if (IsAngleGreater(new_angle, goal_angle)) { | ||
| 86 | new_angle -= modifier_angle * time_difference; | ||
| 87 | if (new_angle < 0) { | ||
| 88 | new_angle += TAU; | ||
| 89 | } | ||
| 90 | if (!IsAngleGreater(new_angle, goal_angle)) { | ||
| 91 | return goal_angle; | ||
| 92 | } | ||
| 93 | } else if (IsAngleSmaller(new_angle, goal_angle)) { | ||
| 94 | new_angle += modifier_angle * time_difference; | ||
| 95 | if (new_angle >= TAU) { | ||
| 96 | new_angle -= TAU; | ||
| 97 | } | ||
| 98 | if (!IsAngleSmaller(new_angle, goal_angle)) { | ||
| 99 | return goal_angle; | ||
| 100 | } | ||
| 101 | } else { | ||
| 102 | return goal_angle; | ||
| 103 | } | ||
| 104 | return new_angle; | ||
| 105 | } | ||
| 106 | |||
| 107 | void SetGoalAngle(bool r, bool l, bool u, bool d) { | ||
| 108 | // Move to the right | ||
| 109 | if (r && !u && !d) { | ||
| 110 | goal_angle = 0.0f; | ||
| 111 | } | ||
| 112 | |||
| 113 | // Move to the upper right | ||
| 114 | if (r && u && !d) { | ||
| 115 | goal_angle = Common::PI * 0.25f; | ||
| 116 | } | ||
| 117 | |||
| 118 | // Move up | ||
| 119 | if (u && !l && !r) { | ||
| 120 | goal_angle = Common::PI * 0.5f; | ||
| 121 | } | ||
| 122 | |||
| 123 | // Move to the upper left | ||
| 124 | if (l && u && !d) { | ||
| 125 | goal_angle = Common::PI * 0.75f; | ||
| 126 | } | ||
| 127 | |||
| 128 | // Move to the left | ||
| 129 | if (l && !u && !d) { | ||
| 130 | goal_angle = Common::PI; | ||
| 131 | } | ||
| 132 | |||
| 133 | // Move to the bottom left | ||
| 134 | if (l && !u && d) { | ||
| 135 | goal_angle = Common::PI * 1.25f; | ||
| 136 | } | ||
| 137 | |||
| 138 | // Move down | ||
| 139 | if (d && !l && !r) { | ||
| 140 | goal_angle = Common::PI * 1.5f; | ||
| 141 | } | ||
| 142 | |||
| 143 | // Move to the bottom right | ||
| 144 | if (r && !u && d) { | ||
| 145 | goal_angle = Common::PI * 1.75f; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | void UpdateUpButtonStatus(const Common::Input::CallbackStatus& button_callback) { | ||
| 150 | up_status = button_callback.button_status.value; | ||
| 151 | UpdateStatus(); | ||
| 152 | } | ||
| 153 | |||
| 154 | void UpdateDownButtonStatus(const Common::Input::CallbackStatus& button_callback) { | ||
| 155 | down_status = button_callback.button_status.value; | ||
| 156 | UpdateStatus(); | ||
| 157 | } | ||
| 158 | |||
| 159 | void UpdateLeftButtonStatus(const Common::Input::CallbackStatus& button_callback) { | ||
| 160 | left_status = button_callback.button_status.value; | ||
| 161 | UpdateStatus(); | ||
| 162 | } | ||
| 163 | |||
| 164 | void UpdateRightButtonStatus(const Common::Input::CallbackStatus& button_callback) { | ||
| 165 | right_status = button_callback.button_status.value; | ||
| 166 | UpdateStatus(); | ||
| 167 | } | ||
| 168 | |||
| 169 | void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) { | ||
| 170 | modifier_status = button_callback.button_status.value; | ||
| 171 | UpdateStatus(); | ||
| 172 | } | ||
| 173 | |||
| 174 | void UpdateStatus() { | ||
| 175 | const float coef = modifier_status ? modifier_scale : 1.0f; | ||
| 176 | |||
| 177 | bool r = right_status; | ||
| 178 | bool l = left_status; | ||
| 179 | bool u = up_status; | ||
| 180 | bool d = down_status; | ||
| 181 | |||
| 182 | // Eliminate contradictory movements | ||
| 183 | if (r && l) { | ||
| 184 | r = false; | ||
| 185 | l = false; | ||
| 186 | } | ||
| 187 | if (u && d) { | ||
| 188 | u = false; | ||
| 189 | d = false; | ||
| 190 | } | ||
| 191 | |||
| 192 | // Move if a key is pressed | ||
| 193 | if (r || l || u || d) { | ||
| 194 | amplitude = coef; | ||
| 195 | } else { | ||
| 196 | amplitude = 0; | ||
| 197 | } | ||
| 198 | |||
| 199 | const auto now = std::chrono::steady_clock::now(); | ||
| 200 | const auto time_difference = static_cast<u64>( | ||
| 201 | std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count()); | ||
| 202 | |||
| 203 | if (time_difference < 10) { | ||
| 204 | // Disable analog mode if inputs are too fast | ||
| 205 | SetGoalAngle(r, l, u, d); | ||
| 206 | angle = goal_angle; | ||
| 207 | } else { | ||
| 208 | angle = GetAngle(now); | ||
| 209 | SetGoalAngle(r, l, u, d); | ||
| 210 | } | ||
| 211 | |||
| 212 | last_update = now; | ||
| 213 | Common::Input::CallbackStatus status{ | ||
| 214 | .type = Common::Input::InputType::Stick, | ||
| 215 | .stick_status = GetStatus(), | ||
| 216 | }; | ||
| 217 | last_x_axis_value = status.stick_status.x.raw_value; | ||
| 218 | last_y_axis_value = status.stick_status.y.raw_value; | ||
| 219 | TriggerOnChange(status); | ||
| 220 | } | ||
| 221 | |||
| 222 | void ForceUpdate() override { | ||
| 223 | up->ForceUpdate(); | ||
| 224 | down->ForceUpdate(); | ||
| 225 | left->ForceUpdate(); | ||
| 226 | right->ForceUpdate(); | ||
| 227 | modifier->ForceUpdate(); | ||
| 228 | } | ||
| 229 | |||
| 230 | void SoftUpdate() override { | ||
| 231 | Common::Input::CallbackStatus status{ | ||
| 232 | .type = Common::Input::InputType::Stick, | ||
| 233 | .stick_status = GetStatus(), | ||
| 234 | }; | ||
| 235 | if (last_x_axis_value == status.stick_status.x.raw_value && | ||
| 236 | last_y_axis_value == status.stick_status.y.raw_value) { | ||
| 237 | return; | ||
| 238 | } | ||
| 239 | last_x_axis_value = status.stick_status.x.raw_value; | ||
| 240 | last_y_axis_value = status.stick_status.y.raw_value; | ||
| 241 | TriggerOnChange(status); | ||
| 242 | } | ||
| 243 | |||
| 244 | Common::Input::StickStatus GetStatus() const { | ||
| 245 | Common::Input::StickStatus status{}; | ||
| 246 | status.x.properties = properties; | ||
| 247 | status.y.properties = properties; | ||
| 248 | if (Settings::values.emulate_analog_keyboard) { | ||
| 249 | const auto now = std::chrono::steady_clock::now(); | ||
| 250 | float angle_ = GetAngle(now); | ||
| 251 | status.x.raw_value = std::cos(angle_) * amplitude; | ||
| 252 | status.y.raw_value = std::sin(angle_) * amplitude; | ||
| 253 | return status; | ||
| 254 | } | ||
| 255 | constexpr float SQRT_HALF = 0.707106781f; | ||
| 256 | int x = 0, y = 0; | ||
| 257 | if (right_status) { | ||
| 258 | ++x; | ||
| 259 | } | ||
| 260 | if (left_status) { | ||
| 261 | --x; | ||
| 262 | } | ||
| 263 | if (up_status) { | ||
| 264 | ++y; | ||
| 265 | } | ||
| 266 | if (down_status) { | ||
| 267 | --y; | ||
| 268 | } | ||
| 269 | const float coef = modifier_status ? modifier_scale : 1.0f; | ||
| 270 | status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); | ||
| 271 | status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); | ||
| 272 | return status; | ||
| 273 | } | ||
| 274 | |||
| 275 | private: | ||
| 276 | Button up; | ||
| 277 | Button down; | ||
| 278 | Button left; | ||
| 279 | Button right; | ||
| 280 | Button modifier; | ||
| 281 | float modifier_scale{}; | ||
| 282 | float modifier_angle{}; | ||
| 283 | float angle{}; | ||
| 284 | float goal_angle{}; | ||
| 285 | float amplitude{}; | ||
| 286 | bool up_status{}; | ||
| 287 | bool down_status{}; | ||
| 288 | bool left_status{}; | ||
| 289 | bool right_status{}; | ||
| 290 | bool modifier_status{}; | ||
| 291 | float last_x_axis_value{}; | ||
| 292 | float last_y_axis_value{}; | ||
| 293 | const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; | ||
| 294 | std::chrono::time_point<std::chrono::steady_clock> last_update; | ||
| 295 | }; | ||
| 296 | |||
| 297 | std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create( | ||
| 298 | const Common::ParamPackage& params) { | ||
| 299 | const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); | ||
| 300 | auto up = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( | ||
| 301 | params.Get("up", null_engine)); | ||
| 302 | auto down = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( | ||
| 303 | params.Get("down", null_engine)); | ||
| 304 | auto left = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( | ||
| 305 | params.Get("left", null_engine)); | ||
| 306 | auto right = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( | ||
| 307 | params.Get("right", null_engine)); | ||
| 308 | auto modifier = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( | ||
| 309 | params.Get("modifier", null_engine)); | ||
| 310 | auto modifier_scale = params.Get("modifier_scale", 0.5f); | ||
| 311 | auto modifier_angle = params.Get("modifier_angle", 5.5f); | ||
| 312 | return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left), | ||
| 313 | std::move(right), std::move(modifier), modifier_scale, | ||
| 314 | modifier_angle); | ||
| 315 | } | ||
| 316 | |||
| 317 | } // namespace InputCommon | ||
diff --git a/src/input_common/analog_from_button.h b/src/input_common/helpers/stick_from_buttons.h index bbd583dd9..437ace4f7 100755..100644 --- a/src/input_common/analog_from_button.h +++ b/src/input_common/helpers/stick_from_buttons.h | |||
| @@ -4,8 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include "common/input.h" |
| 8 | #include "core/frontend/input.h" | ||
| 9 | 8 | ||
| 10 | namespace InputCommon { | 9 | namespace InputCommon { |
| 11 | 10 | ||
| @@ -13,7 +12,7 @@ namespace InputCommon { | |||
| 13 | * An analog device factory that takes direction button devices and combines them into a analog | 12 | * An analog device factory that takes direction button devices and combines them into a analog |
| 14 | * device. | 13 | * device. |
| 15 | */ | 14 | */ |
| 16 | class AnalogFromButton final : public Input::Factory<Input::AnalogDevice> { | 15 | class StickFromButton final : public Common::Input::Factory<Common::Input::InputDevice> { |
| 17 | public: | 16 | public: |
| 18 | /** | 17 | /** |
| 19 | * Creates an analog device from direction button devices | 18 | * Creates an analog device from direction button devices |
| @@ -25,7 +24,7 @@ public: | |||
| 25 | * - "modifier": a serialized ParamPackage for creating a button device as the modifier | 24 | * - "modifier": a serialized ParamPackage for creating a button device as the modifier |
| 26 | * - "modifier_scale": a float for the multiplier the modifier gives to the position | 25 | * - "modifier_scale": a float for the multiplier the modifier gives to the position |
| 27 | */ | 26 | */ |
| 28 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; | 27 | std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override; |
| 29 | }; | 28 | }; |
| 30 | 29 | ||
| 31 | } // namespace InputCommon | 30 | } // namespace InputCommon |
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp new file mode 100644 index 000000000..ece1e3b32 --- /dev/null +++ b/src/input_common/helpers/touch_from_buttons.cpp | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | // Copyright 2020 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include "common/settings.h" | ||
| 7 | #include "core/frontend/framebuffer_layout.h" | ||
| 8 | #include "input_common/helpers/touch_from_buttons.h" | ||
| 9 | |||
| 10 | namespace InputCommon { | ||
| 11 | |||
| 12 | class TouchFromButtonDevice final : public Common::Input::InputDevice { | ||
| 13 | public: | ||
| 14 | using Button = std::unique_ptr<Common::Input::InputDevice>; | ||
| 15 | TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_) | ||
| 16 | : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) { | ||
| 17 | last_button_value = false; | ||
| 18 | button->SetCallback({ | ||
| 19 | .on_change = | ||
| 20 | [this](const Common::Input::CallbackStatus& callback_) { | ||
| 21 | UpdateButtonStatus(callback_); | ||
| 22 | }, | ||
| 23 | }); | ||
| 24 | button->ForceUpdate(); | ||
| 25 | } | ||
| 26 | |||
| 27 | void ForceUpdate() override { | ||
| 28 | button->ForceUpdate(); | ||
| 29 | } | ||
| 30 | |||
| 31 | Common::Input::TouchStatus GetStatus(bool pressed) const { | ||
| 32 | const Common::Input::ButtonStatus button_status{ | ||
| 33 | .value = pressed, | ||
| 34 | }; | ||
| 35 | Common::Input::TouchStatus status{ | ||
| 36 | .pressed = button_status, | ||
| 37 | .x = {}, | ||
| 38 | .y = {}, | ||
| 39 | .id = touch_id, | ||
| 40 | }; | ||
| 41 | status.x.properties = properties; | ||
| 42 | status.y.properties = properties; | ||
| 43 | |||
| 44 | if (!pressed) { | ||
| 45 | return status; | ||
| 46 | } | ||
| 47 | |||
| 48 | status.x.raw_value = x; | ||
| 49 | status.y.raw_value = y; | ||
| 50 | return status; | ||
| 51 | } | ||
| 52 | |||
| 53 | void UpdateButtonStatus(const Common::Input::CallbackStatus& button_callback) { | ||
| 54 | const Common::Input::CallbackStatus status{ | ||
| 55 | .type = Common::Input::InputType::Touch, | ||
| 56 | .touch_status = GetStatus(button_callback.button_status.value), | ||
| 57 | }; | ||
| 58 | if (last_button_value != button_callback.button_status.value) { | ||
| 59 | last_button_value = button_callback.button_status.value; | ||
| 60 | TriggerOnChange(status); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | private: | ||
| 65 | Button button; | ||
| 66 | bool last_button_value; | ||
| 67 | const int touch_id; | ||
| 68 | const float x; | ||
| 69 | const float y; | ||
| 70 | const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; | ||
| 71 | }; | ||
| 72 | |||
| 73 | std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create( | ||
| 74 | const Common::ParamPackage& params) { | ||
| 75 | const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); | ||
| 76 | auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( | ||
| 77 | params.Get("button", null_engine)); | ||
| 78 | const auto touch_id = params.Get("touch_id", 0); | ||
| 79 | const float x = params.Get("x", 0.0f) / 1280.0f; | ||
| 80 | const float y = params.Get("y", 0.0f) / 720.0f; | ||
| 81 | return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y); | ||
| 82 | } | ||
| 83 | |||
| 84 | } // namespace InputCommon | ||
diff --git a/src/input_common/touch_from_button.h b/src/input_common/helpers/touch_from_buttons.h index 8b4d1aa96..628f18215 100644 --- a/src/input_common/touch_from_button.h +++ b/src/input_common/helpers/touch_from_buttons.h | |||
| @@ -4,20 +4,19 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include "common/input.h" |
| 8 | #include "core/frontend/input.h" | ||
| 9 | 8 | ||
| 10 | namespace InputCommon { | 9 | namespace InputCommon { |
| 11 | 10 | ||
| 12 | /** | 11 | /** |
| 13 | * A touch device factory that takes a list of button devices and combines them into a touch device. | 12 | * A touch device factory that takes a list of button devices and combines them into a touch device. |
| 14 | */ | 13 | */ |
| 15 | class TouchFromButtonFactory final : public Input::Factory<Input::TouchDevice> { | 14 | class TouchFromButton final : public Common::Input::Factory<Common::Input::InputDevice> { |
| 16 | public: | 15 | public: |
| 17 | /** | 16 | /** |
| 18 | * Creates a touch device from a list of button devices | 17 | * Creates a touch device from a list of button devices |
| 19 | */ | 18 | */ |
| 20 | std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override; | 19 | std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override; |
| 21 | }; | 20 | }; |
| 22 | 21 | ||
| 23 | } // namespace InputCommon | 22 | } // namespace InputCommon |
diff --git a/src/input_common/udp/protocol.cpp b/src/input_common/helpers/udp_protocol.cpp index 5e50bd612..cdeab7e11 100644 --- a/src/input_common/udp/protocol.cpp +++ b/src/input_common/helpers/udp_protocol.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include <cstddef> | 5 | #include <cstddef> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "input_common/udp/protocol.h" | 8 | #include "input_common/helpers/udp_protocol.h" |
| 9 | 9 | ||
| 10 | namespace InputCommon::CemuhookUDP { | 10 | namespace InputCommon::CemuhookUDP { |
| 11 | 11 | ||
diff --git a/src/input_common/udp/protocol.h b/src/input_common/helpers/udp_protocol.h index 1bdc9209e..bcba12c58 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/helpers/udp_protocol.h | |||
| @@ -56,6 +56,12 @@ constexpr Type GetMessageType(); | |||
| 56 | 56 | ||
| 57 | namespace Request { | 57 | namespace Request { |
| 58 | 58 | ||
| 59 | enum RegisterFlags : u8 { | ||
| 60 | AllPads, | ||
| 61 | PadID, | ||
| 62 | PadMACAdddress, | ||
| 63 | }; | ||
| 64 | |||
| 59 | struct Version {}; | 65 | struct Version {}; |
| 60 | /** | 66 | /** |
| 61 | * Requests the server to send information about what controllers are plugged into the ports | 67 | * Requests the server to send information about what controllers are plugged into the ports |
| @@ -77,13 +83,8 @@ static_assert(std::is_trivially_copyable_v<PortInfo>, | |||
| 77 | * timeout seems to be 5 seconds. | 83 | * timeout seems to be 5 seconds. |
| 78 | */ | 84 | */ |
| 79 | struct PadData { | 85 | struct PadData { |
| 80 | enum class Flags : u8 { | ||
| 81 | AllPorts, | ||
| 82 | Id, | ||
| 83 | Mac, | ||
| 84 | }; | ||
| 85 | /// Determines which method will be used as a look up for the controller | 86 | /// Determines which method will be used as a look up for the controller |
| 86 | Flags flags{}; | 87 | RegisterFlags flags{}; |
| 87 | /// Index of the port of the controller to retrieve data about | 88 | /// Index of the port of the controller to retrieve data about |
| 88 | u8 port_id{}; | 89 | u8 port_id{}; |
| 89 | /// Mac address of the controller to retrieve data about | 90 | /// Mac address of the controller to retrieve data about |
| @@ -113,6 +114,36 @@ Message<T> Create(const T data, const u32 client_id = 0) { | |||
| 113 | 114 | ||
| 114 | namespace Response { | 115 | namespace Response { |
| 115 | 116 | ||
| 117 | enum class ConnectionType : u8 { | ||
| 118 | None, | ||
| 119 | Usb, | ||
| 120 | Bluetooth, | ||
| 121 | }; | ||
| 122 | |||
| 123 | enum class State : u8 { | ||
| 124 | Disconnected, | ||
| 125 | Reserved, | ||
| 126 | Connected, | ||
| 127 | }; | ||
| 128 | |||
| 129 | enum class Model : u8 { | ||
| 130 | None, | ||
| 131 | PartialGyro, | ||
| 132 | FullGyro, | ||
| 133 | Generic, | ||
| 134 | }; | ||
| 135 | |||
| 136 | enum class Battery : u8 { | ||
| 137 | None = 0x00, | ||
| 138 | Dying = 0x01, | ||
| 139 | Low = 0x02, | ||
| 140 | Medium = 0x03, | ||
| 141 | High = 0x04, | ||
| 142 | Full = 0x05, | ||
| 143 | Charging = 0xEE, | ||
| 144 | Charged = 0xEF, | ||
| 145 | }; | ||
| 146 | |||
| 116 | struct Version { | 147 | struct Version { |
| 117 | u16_le version{}; | 148 | u16_le version{}; |
| 118 | }; | 149 | }; |
| @@ -122,11 +153,11 @@ static_assert(std::is_trivially_copyable_v<Version>, | |||
| 122 | 153 | ||
| 123 | struct PortInfo { | 154 | struct PortInfo { |
| 124 | u8 id{}; | 155 | u8 id{}; |
| 125 | u8 state{}; | 156 | State state{}; |
| 126 | u8 model{}; | 157 | Model model{}; |
| 127 | u8 connection_type{}; | 158 | ConnectionType connection_type{}; |
| 128 | MacAddress mac; | 159 | MacAddress mac; |
| 129 | u8 battery{}; | 160 | Battery battery{}; |
| 130 | u8 is_pad_active{}; | 161 | u8 is_pad_active{}; |
| 131 | }; | 162 | }; |
| 132 | static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size"); | 163 | static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size"); |
| @@ -177,18 +208,18 @@ struct PadData { | |||
| 177 | u8 right_stick_y{}; | 208 | u8 right_stick_y{}; |
| 178 | 209 | ||
| 179 | struct AnalogButton { | 210 | struct AnalogButton { |
| 180 | u8 button_8{}; | 211 | u8 button_dpad_left_analog{}; |
| 181 | u8 button_7{}; | 212 | u8 button_dpad_down_analog{}; |
| 182 | u8 button_6{}; | 213 | u8 button_dpad_right_analog{}; |
| 183 | u8 button_5{}; | 214 | u8 button_dpad_up_analog{}; |
| 184 | u8 button_12{}; | 215 | u8 button_square_analog{}; |
| 185 | u8 button_11{}; | 216 | u8 button_cross_analog{}; |
| 186 | u8 button_10{}; | 217 | u8 button_circle_analog{}; |
| 187 | u8 button_9{}; | 218 | u8 button_triangle_analog{}; |
| 188 | u8 button_16{}; | 219 | u8 button_r1_analog{}; |
| 189 | u8 button_15{}; | 220 | u8 button_l1_analog{}; |
| 190 | u8 button_14{}; | 221 | u8 trigger_r2{}; |
| 191 | u8 button_13{}; | 222 | u8 trigger_l2{}; |
| 192 | } analog_button; | 223 | } analog_button; |
| 193 | 224 | ||
| 194 | std::array<TouchPad, 2> touch; | 225 | std::array<TouchPad, 2> touch; |
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp new file mode 100644 index 000000000..9c17ca4f7 --- /dev/null +++ b/src/input_common/input_engine.cpp | |||
| @@ -0,0 +1,362 @@ | |||
| 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/logging/log.h" | ||
| 6 | #include "common/param_package.h" | ||
| 7 | #include "input_common/input_engine.h" | ||
| 8 | |||
| 9 | namespace InputCommon { | ||
| 10 | |||
| 11 | void InputEngine::PreSetController(const PadIdentifier& identifier) { | ||
| 12 | std::lock_guard lock{mutex}; | ||
| 13 | controller_list.try_emplace(identifier); | ||
| 14 | } | ||
| 15 | |||
| 16 | void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) { | ||
| 17 | std::lock_guard lock{mutex}; | ||
| 18 | ControllerData& controller = controller_list.at(identifier); | ||
| 19 | controller.buttons.try_emplace(button, false); | ||
| 20 | } | ||
| 21 | |||
| 22 | void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) { | ||
| 23 | std::lock_guard lock{mutex}; | ||
| 24 | ControllerData& controller = controller_list.at(identifier); | ||
| 25 | controller.hat_buttons.try_emplace(button, u8{0}); | ||
| 26 | } | ||
| 27 | |||
| 28 | void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) { | ||
| 29 | std::lock_guard lock{mutex}; | ||
| 30 | ControllerData& controller = controller_list.at(identifier); | ||
| 31 | controller.axes.try_emplace(axis, 0.0f); | ||
| 32 | } | ||
| 33 | |||
| 34 | void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) { | ||
| 35 | std::lock_guard lock{mutex}; | ||
| 36 | ControllerData& controller = controller_list.at(identifier); | ||
| 37 | controller.motions.try_emplace(motion); | ||
| 38 | } | ||
| 39 | |||
| 40 | void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) { | ||
| 41 | { | ||
| 42 | std::lock_guard lock{mutex}; | ||
| 43 | ControllerData& controller = controller_list.at(identifier); | ||
| 44 | if (!configuring) { | ||
| 45 | controller.buttons.insert_or_assign(button, value); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | TriggerOnButtonChange(identifier, button, value); | ||
| 49 | } | ||
| 50 | |||
| 51 | void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) { | ||
| 52 | { | ||
| 53 | std::lock_guard lock{mutex}; | ||
| 54 | ControllerData& controller = controller_list.at(identifier); | ||
| 55 | if (!configuring) { | ||
| 56 | controller.hat_buttons.insert_or_assign(button, value); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | TriggerOnHatButtonChange(identifier, button, value); | ||
| 60 | } | ||
| 61 | |||
| 62 | void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) { | ||
| 63 | { | ||
| 64 | std::lock_guard lock{mutex}; | ||
| 65 | ControllerData& controller = controller_list.at(identifier); | ||
| 66 | if (!configuring) { | ||
| 67 | controller.axes.insert_or_assign(axis, value); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | TriggerOnAxisChange(identifier, axis, value); | ||
| 71 | } | ||
| 72 | |||
| 73 | void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) { | ||
| 74 | { | ||
| 75 | std::lock_guard lock{mutex}; | ||
| 76 | ControllerData& controller = controller_list.at(identifier); | ||
| 77 | if (!configuring) { | ||
| 78 | controller.battery = value; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | TriggerOnBatteryChange(identifier, value); | ||
| 82 | } | ||
| 83 | |||
| 84 | void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { | ||
| 85 | { | ||
| 86 | std::lock_guard lock{mutex}; | ||
| 87 | ControllerData& controller = controller_list.at(identifier); | ||
| 88 | if (!configuring) { | ||
| 89 | controller.motions.insert_or_assign(motion, value); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | TriggerOnMotionChange(identifier, motion, value); | ||
| 93 | } | ||
| 94 | |||
| 95 | bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { | ||
| 96 | std::lock_guard lock{mutex}; | ||
| 97 | const auto controller_iter = controller_list.find(identifier); | ||
| 98 | if (controller_iter == controller_list.cend()) { | ||
| 99 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | ||
| 100 | identifier.pad, identifier.port); | ||
| 101 | return false; | ||
| 102 | } | ||
| 103 | const ControllerData& controller = controller_iter->second; | ||
| 104 | const auto button_iter = controller.buttons.find(button); | ||
| 105 | if (button_iter == controller.buttons.cend()) { | ||
| 106 | LOG_ERROR(Input, "Invalid button {}", button); | ||
| 107 | return false; | ||
| 108 | } | ||
| 109 | return button_iter->second; | ||
| 110 | } | ||
| 111 | |||
| 112 | bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const { | ||
| 113 | std::lock_guard lock{mutex}; | ||
| 114 | const auto controller_iter = controller_list.find(identifier); | ||
| 115 | if (controller_iter == controller_list.cend()) { | ||
| 116 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | ||
| 117 | identifier.pad, identifier.port); | ||
| 118 | return false; | ||
| 119 | } | ||
| 120 | const ControllerData& controller = controller_iter->second; | ||
| 121 | const auto hat_iter = controller.hat_buttons.find(button); | ||
| 122 | if (hat_iter == controller.hat_buttons.cend()) { | ||
| 123 | LOG_ERROR(Input, "Invalid hat button {}", button); | ||
| 124 | return false; | ||
| 125 | } | ||
| 126 | return (hat_iter->second & direction) != 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { | ||
| 130 | std::lock_guard lock{mutex}; | ||
| 131 | const auto controller_iter = controller_list.find(identifier); | ||
| 132 | if (controller_iter == controller_list.cend()) { | ||
| 133 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | ||
| 134 | identifier.pad, identifier.port); | ||
| 135 | return 0.0f; | ||
| 136 | } | ||
| 137 | const ControllerData& controller = controller_iter->second; | ||
| 138 | const auto axis_iter = controller.axes.find(axis); | ||
| 139 | if (axis_iter == controller.axes.cend()) { | ||
| 140 | LOG_ERROR(Input, "Invalid axis {}", axis); | ||
| 141 | return 0.0f; | ||
| 142 | } | ||
| 143 | return axis_iter->second; | ||
| 144 | } | ||
| 145 | |||
| 146 | BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { | ||
| 147 | std::lock_guard lock{mutex}; | ||
| 148 | const auto controller_iter = controller_list.find(identifier); | ||
| 149 | if (controller_iter == controller_list.cend()) { | ||
| 150 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | ||
| 151 | identifier.pad, identifier.port); | ||
| 152 | return BatteryLevel::Charging; | ||
| 153 | } | ||
| 154 | const ControllerData& controller = controller_iter->second; | ||
| 155 | return controller.battery; | ||
| 156 | } | ||
| 157 | |||
| 158 | BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { | ||
| 159 | std::lock_guard lock{mutex}; | ||
| 160 | const auto controller_iter = controller_list.find(identifier); | ||
| 161 | if (controller_iter == controller_list.cend()) { | ||
| 162 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), | ||
| 163 | identifier.pad, identifier.port); | ||
| 164 | return {}; | ||
| 165 | } | ||
| 166 | const ControllerData& controller = controller_iter->second; | ||
| 167 | return controller.motions.at(motion); | ||
| 168 | } | ||
| 169 | |||
| 170 | void InputEngine::ResetButtonState() { | ||
| 171 | for (const auto& controller : controller_list) { | ||
| 172 | for (const auto& button : controller.second.buttons) { | ||
| 173 | SetButton(controller.first, button.first, false); | ||
| 174 | } | ||
| 175 | for (const auto& button : controller.second.hat_buttons) { | ||
| 176 | SetHatButton(controller.first, button.first, false); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | void InputEngine::ResetAnalogState() { | ||
| 182 | for (const auto& controller : controller_list) { | ||
| 183 | for (const auto& axis : controller.second.axes) { | ||
| 184 | SetAxis(controller.first, axis.first, 0.0); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) { | ||
| 190 | std::lock_guard lock{mutex_callback}; | ||
| 191 | for (const auto& poller_pair : callback_list) { | ||
| 192 | const InputIdentifier& poller = poller_pair.second; | ||
| 193 | if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) { | ||
| 194 | continue; | ||
| 195 | } | ||
| 196 | if (poller.callback.on_change) { | ||
| 197 | poller.callback.on_change(); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | if (!configuring || !mapping_callback.on_data) { | ||
| 201 | return; | ||
| 202 | } | ||
| 203 | |||
| 204 | PreSetButton(identifier, button); | ||
| 205 | if (value == GetButton(identifier, button)) { | ||
| 206 | return; | ||
| 207 | } | ||
| 208 | mapping_callback.on_data(MappingData{ | ||
| 209 | .engine = GetEngineName(), | ||
| 210 | .pad = identifier, | ||
| 211 | .type = EngineInputType::Button, | ||
| 212 | .index = button, | ||
| 213 | .button_value = value, | ||
| 214 | }); | ||
| 215 | } | ||
| 216 | |||
| 217 | void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) { | ||
| 218 | std::lock_guard lock{mutex_callback}; | ||
| 219 | for (const auto& poller_pair : callback_list) { | ||
| 220 | const InputIdentifier& poller = poller_pair.second; | ||
| 221 | if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) { | ||
| 222 | continue; | ||
| 223 | } | ||
| 224 | if (poller.callback.on_change) { | ||
| 225 | poller.callback.on_change(); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | if (!configuring || !mapping_callback.on_data) { | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | for (std::size_t index = 1; index < 0xff; index <<= 1) { | ||
| 232 | bool button_value = (value & index) != 0; | ||
| 233 | if (button_value == GetHatButton(identifier, button, static_cast<u8>(index))) { | ||
| 234 | continue; | ||
| 235 | } | ||
| 236 | mapping_callback.on_data(MappingData{ | ||
| 237 | .engine = GetEngineName(), | ||
| 238 | .pad = identifier, | ||
| 239 | .type = EngineInputType::HatButton, | ||
| 240 | .index = button, | ||
| 241 | .hat_name = GetHatButtonName(static_cast<u8>(index)), | ||
| 242 | }); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) { | ||
| 247 | std::lock_guard lock{mutex_callback}; | ||
| 248 | for (const auto& poller_pair : callback_list) { | ||
| 249 | const InputIdentifier& poller = poller_pair.second; | ||
| 250 | if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) { | ||
| 251 | continue; | ||
| 252 | } | ||
| 253 | if (poller.callback.on_change) { | ||
| 254 | poller.callback.on_change(); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | if (!configuring || !mapping_callback.on_data) { | ||
| 258 | return; | ||
| 259 | } | ||
| 260 | if (std::abs(value - GetAxis(identifier, axis)) < 0.5f) { | ||
| 261 | return; | ||
| 262 | } | ||
| 263 | mapping_callback.on_data(MappingData{ | ||
| 264 | .engine = GetEngineName(), | ||
| 265 | .pad = identifier, | ||
| 266 | .type = EngineInputType::Analog, | ||
| 267 | .index = axis, | ||
| 268 | .axis_value = value, | ||
| 269 | }); | ||
| 270 | } | ||
| 271 | |||
| 272 | void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, | ||
| 273 | [[maybe_unused]] BatteryLevel value) { | ||
| 274 | std::lock_guard lock{mutex_callback}; | ||
| 275 | for (const auto& poller_pair : callback_list) { | ||
| 276 | const InputIdentifier& poller = poller_pair.second; | ||
| 277 | if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) { | ||
| 278 | continue; | ||
| 279 | } | ||
| 280 | if (poller.callback.on_change) { | ||
| 281 | poller.callback.on_change(); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, | ||
| 287 | const BasicMotion& value) { | ||
| 288 | std::lock_guard lock{mutex_callback}; | ||
| 289 | for (const auto& poller_pair : callback_list) { | ||
| 290 | const InputIdentifier& poller = poller_pair.second; | ||
| 291 | if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) { | ||
| 292 | continue; | ||
| 293 | } | ||
| 294 | if (poller.callback.on_change) { | ||
| 295 | poller.callback.on_change(); | ||
| 296 | } | ||
| 297 | } | ||
| 298 | if (!configuring || !mapping_callback.on_data) { | ||
| 299 | return; | ||
| 300 | } | ||
| 301 | if (std::abs(value.gyro_x) < 0.6f && std::abs(value.gyro_y) < 0.6f && | ||
| 302 | std::abs(value.gyro_z) < 0.6f) { | ||
| 303 | return; | ||
| 304 | } | ||
| 305 | mapping_callback.on_data(MappingData{ | ||
| 306 | .engine = GetEngineName(), | ||
| 307 | .pad = identifier, | ||
| 308 | .type = EngineInputType::Motion, | ||
| 309 | .index = motion, | ||
| 310 | .motion_value = value, | ||
| 311 | }); | ||
| 312 | } | ||
| 313 | |||
| 314 | bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, | ||
| 315 | const PadIdentifier& identifier, EngineInputType type, | ||
| 316 | int index) const { | ||
| 317 | if (input_identifier.type != type) { | ||
| 318 | return false; | ||
| 319 | } | ||
| 320 | if (input_identifier.index != index) { | ||
| 321 | return false; | ||
| 322 | } | ||
| 323 | if (input_identifier.identifier != identifier) { | ||
| 324 | return false; | ||
| 325 | } | ||
| 326 | return true; | ||
| 327 | } | ||
| 328 | |||
| 329 | void InputEngine::BeginConfiguration() { | ||
| 330 | configuring = true; | ||
| 331 | } | ||
| 332 | |||
| 333 | void InputEngine::EndConfiguration() { | ||
| 334 | configuring = false; | ||
| 335 | } | ||
| 336 | |||
| 337 | const std::string& InputEngine::GetEngineName() const { | ||
| 338 | return input_engine; | ||
| 339 | } | ||
| 340 | |||
| 341 | int InputEngine::SetCallback(InputIdentifier input_identifier) { | ||
| 342 | std::lock_guard lock{mutex_callback}; | ||
| 343 | callback_list.insert_or_assign(last_callback_key, std::move(input_identifier)); | ||
| 344 | return last_callback_key++; | ||
| 345 | } | ||
| 346 | |||
| 347 | void InputEngine::SetMappingCallback(MappingCallback callback) { | ||
| 348 | std::lock_guard lock{mutex_callback}; | ||
| 349 | mapping_callback = std::move(callback); | ||
| 350 | } | ||
| 351 | |||
| 352 | void InputEngine::DeleteCallback(int key) { | ||
| 353 | std::lock_guard lock{mutex_callback}; | ||
| 354 | const auto& iterator = callback_list.find(key); | ||
| 355 | if (iterator == callback_list.end()) { | ||
| 356 | LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); | ||
| 357 | return; | ||
| 358 | } | ||
| 359 | callback_list.erase(iterator); | ||
| 360 | } | ||
| 361 | |||
| 362 | } // namespace InputCommon | ||
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h new file mode 100644 index 000000000..390581c94 --- /dev/null +++ b/src/input_common/input_engine.h | |||
| @@ -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 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <mutex> | ||
| 9 | #include <unordered_map> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/input.h" | ||
| 13 | #include "common/param_package.h" | ||
| 14 | #include "common/uuid.h" | ||
| 15 | #include "input_common/main.h" | ||
| 16 | |||
| 17 | // Pad Identifier of data source | ||
| 18 | struct PadIdentifier { | ||
| 19 | Common::UUID guid{}; | ||
| 20 | std::size_t port{}; | ||
| 21 | std::size_t pad{}; | ||
| 22 | |||
| 23 | friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default; | ||
| 24 | }; | ||
| 25 | |||
| 26 | // Basic motion data containing data from the sensors and a timestamp in microseconds | ||
| 27 | struct BasicMotion { | ||
| 28 | float gyro_x{}; | ||
| 29 | float gyro_y{}; | ||
| 30 | float gyro_z{}; | ||
| 31 | float accel_x{}; | ||
| 32 | float accel_y{}; | ||
| 33 | float accel_z{}; | ||
| 34 | u64 delta_timestamp{}; | ||
| 35 | }; | ||
| 36 | |||
| 37 | // Stages of a battery charge | ||
| 38 | enum class BatteryLevel { | ||
| 39 | Empty, | ||
| 40 | Critical, | ||
| 41 | Low, | ||
| 42 | Medium, | ||
| 43 | Full, | ||
| 44 | Charging, | ||
| 45 | }; | ||
| 46 | |||
| 47 | // Types of input that are stored in the engine | ||
| 48 | enum class EngineInputType { | ||
| 49 | None, | ||
| 50 | Button, | ||
| 51 | HatButton, | ||
| 52 | Analog, | ||
| 53 | Motion, | ||
| 54 | Battery, | ||
| 55 | }; | ||
| 56 | |||
| 57 | namespace std { | ||
| 58 | // Hash used to create lists from PadIdentifier data | ||
| 59 | template <> | ||
| 60 | struct hash<PadIdentifier> { | ||
| 61 | size_t operator()(const PadIdentifier& pad_id) const noexcept { | ||
| 62 | u64 hash_value = pad_id.guid.uuid[1] ^ pad_id.guid.uuid[0]; | ||
| 63 | hash_value ^= (static_cast<u64>(pad_id.port) << 32); | ||
| 64 | hash_value ^= static_cast<u64>(pad_id.pad); | ||
| 65 | return static_cast<size_t>(hash_value); | ||
| 66 | } | ||
| 67 | }; | ||
| 68 | |||
| 69 | } // namespace std | ||
| 70 | |||
| 71 | namespace InputCommon { | ||
| 72 | |||
| 73 | // Data from the engine and device needed for creating a ParamPackage | ||
| 74 | struct MappingData { | ||
| 75 | std::string engine{}; | ||
| 76 | PadIdentifier pad{}; | ||
| 77 | EngineInputType type{}; | ||
| 78 | int index{}; | ||
| 79 | bool button_value{}; | ||
| 80 | std::string hat_name{}; | ||
| 81 | f32 axis_value{}; | ||
| 82 | BasicMotion motion_value{}; | ||
| 83 | }; | ||
| 84 | |||
| 85 | // Triggered if data changed on the controller | ||
| 86 | struct UpdateCallback { | ||
| 87 | std::function<void()> on_change; | ||
| 88 | }; | ||
| 89 | |||
| 90 | // Triggered if data changed on the controller and the engine is on configuring mode | ||
| 91 | struct MappingCallback { | ||
| 92 | std::function<void(MappingData)> on_data; | ||
| 93 | }; | ||
| 94 | |||
| 95 | // Input Identifier of data source | ||
| 96 | struct InputIdentifier { | ||
| 97 | PadIdentifier identifier; | ||
| 98 | EngineInputType type; | ||
| 99 | int index; | ||
| 100 | UpdateCallback callback; | ||
| 101 | }; | ||
| 102 | |||
| 103 | class InputEngine { | ||
| 104 | public: | ||
| 105 | explicit InputEngine(std::string input_engine_) : input_engine{std::move(input_engine_)} {} | ||
| 106 | |||
| 107 | virtual ~InputEngine() = default; | ||
| 108 | |||
| 109 | // Enable configuring mode for mapping | ||
| 110 | void BeginConfiguration(); | ||
| 111 | |||
| 112 | // Disable configuring mode for mapping | ||
| 113 | void EndConfiguration(); | ||
| 114 | |||
| 115 | // Sets a led pattern for a controller | ||
| 116 | virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier, | ||
| 117 | [[maybe_unused]] const Common::Input::LedStatus& led_status) {} | ||
| 118 | |||
| 119 | // Sets rumble to a controller | ||
| 120 | virtual Common::Input::VibrationError SetRumble( | ||
| 121 | [[maybe_unused]] const PadIdentifier& identifier, | ||
| 122 | [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { | ||
| 123 | return Common::Input::VibrationError::NotSupported; | ||
| 124 | } | ||
| 125 | |||
| 126 | // Sets polling mode to a controller | ||
| 127 | virtual Common::Input::PollingError SetPollingMode( | ||
| 128 | [[maybe_unused]] const PadIdentifier& identifier, | ||
| 129 | [[maybe_unused]] const Common::Input::PollingMode vibration) { | ||
| 130 | return Common::Input::PollingError::NotSupported; | ||
| 131 | } | ||
| 132 | |||
| 133 | // Returns the engine name | ||
| 134 | [[nodiscard]] const std::string& GetEngineName() const; | ||
| 135 | |||
| 136 | /// Used for automapping features | ||
| 137 | virtual std::vector<Common::ParamPackage> GetInputDevices() const { | ||
| 138 | return {}; | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Retrieves the button mappings for the given device | ||
| 142 | virtual ButtonMapping GetButtonMappingForDevice( | ||
| 143 | [[maybe_unused]] const Common::ParamPackage& params) { | ||
| 144 | return {}; | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Retrieves the analog mappings for the given device | ||
| 148 | virtual AnalogMapping GetAnalogMappingForDevice( | ||
| 149 | [[maybe_unused]] const Common::ParamPackage& params) { | ||
| 150 | return {}; | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Retrieves the motion mappings for the given device | ||
| 154 | virtual MotionMapping GetMotionMappingForDevice( | ||
| 155 | [[maybe_unused]] const Common::ParamPackage& params) { | ||
| 156 | return {}; | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Retrieves the name of the given input. | ||
| 160 | virtual Common::Input::ButtonNames GetUIName( | ||
| 161 | [[maybe_unused]] const Common::ParamPackage& params) const { | ||
| 162 | return Common::Input::ButtonNames::Engine; | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Retrieves the index number of the given hat button direction | ||
| 166 | virtual u8 GetHatButtonId([[maybe_unused]] const std::string& direction_name) const { | ||
| 167 | return 0; | ||
| 168 | } | ||
| 169 | |||
| 170 | void PreSetController(const PadIdentifier& identifier); | ||
| 171 | void PreSetButton(const PadIdentifier& identifier, int button); | ||
| 172 | void PreSetHatButton(const PadIdentifier& identifier, int button); | ||
| 173 | void PreSetAxis(const PadIdentifier& identifier, int axis); | ||
| 174 | void PreSetMotion(const PadIdentifier& identifier, int motion); | ||
| 175 | void ResetButtonState(); | ||
| 176 | void ResetAnalogState(); | ||
| 177 | |||
| 178 | bool GetButton(const PadIdentifier& identifier, int button) const; | ||
| 179 | bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; | ||
| 180 | f32 GetAxis(const PadIdentifier& identifier, int axis) const; | ||
| 181 | BatteryLevel GetBattery(const PadIdentifier& identifier) const; | ||
| 182 | BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; | ||
| 183 | |||
| 184 | int SetCallback(InputIdentifier input_identifier); | ||
| 185 | void SetMappingCallback(MappingCallback callback); | ||
| 186 | void DeleteCallback(int key); | ||
| 187 | |||
| 188 | protected: | ||
| 189 | void SetButton(const PadIdentifier& identifier, int button, bool value); | ||
| 190 | void SetHatButton(const PadIdentifier& identifier, int button, u8 value); | ||
| 191 | void SetAxis(const PadIdentifier& identifier, int axis, f32 value); | ||
| 192 | void SetBattery(const PadIdentifier& identifier, BatteryLevel value); | ||
| 193 | void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); | ||
| 194 | |||
| 195 | virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { | ||
| 196 | return "Unknown"; | ||
| 197 | } | ||
| 198 | |||
| 199 | private: | ||
| 200 | struct ControllerData { | ||
| 201 | std::unordered_map<int, bool> buttons; | ||
| 202 | std::unordered_map<int, u8> hat_buttons; | ||
| 203 | std::unordered_map<int, float> axes; | ||
| 204 | std::unordered_map<int, BasicMotion> motions; | ||
| 205 | BatteryLevel battery{}; | ||
| 206 | }; | ||
| 207 | |||
| 208 | void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); | ||
| 209 | void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); | ||
| 210 | void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); | ||
| 211 | void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value); | ||
| 212 | void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, | ||
| 213 | const BasicMotion& value); | ||
| 214 | |||
| 215 | bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, | ||
| 216 | const PadIdentifier& identifier, EngineInputType type, | ||
| 217 | int index) const; | ||
| 218 | |||
| 219 | mutable std::mutex mutex; | ||
| 220 | mutable std::mutex mutex_callback; | ||
| 221 | bool configuring{false}; | ||
| 222 | const std::string input_engine; | ||
| 223 | int last_callback_key = 0; | ||
| 224 | std::unordered_map<PadIdentifier, ControllerData> controller_list; | ||
| 225 | std::unordered_map<int, InputIdentifier> callback_list; | ||
| 226 | MappingCallback mapping_callback; | ||
| 227 | }; | ||
| 228 | |||
| 229 | } // namespace InputCommon | ||
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp new file mode 100644 index 000000000..6e0024b2d --- /dev/null +++ b/src/input_common/input_mapping.cpp | |||
| @@ -0,0 +1,207 @@ | |||
| 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/common_types.h" | ||
| 6 | #include "common/settings.h" | ||
| 7 | #include "input_common/input_engine.h" | ||
| 8 | #include "input_common/input_mapping.h" | ||
| 9 | |||
| 10 | namespace InputCommon { | ||
| 11 | |||
| 12 | MappingFactory::MappingFactory() {} | ||
| 13 | |||
| 14 | void MappingFactory::BeginMapping(Polling::InputType type) { | ||
| 15 | is_enabled = true; | ||
| 16 | input_type = type; | ||
| 17 | input_queue.Clear(); | ||
| 18 | first_axis = -1; | ||
| 19 | second_axis = -1; | ||
| 20 | } | ||
| 21 | |||
| 22 | [[nodiscard]] const Common::ParamPackage MappingFactory::GetNextInput() { | ||
| 23 | Common::ParamPackage input; | ||
| 24 | input_queue.Pop(input); | ||
| 25 | return input; | ||
| 26 | } | ||
| 27 | |||
| 28 | void MappingFactory::RegisterInput(const MappingData& data) { | ||
| 29 | if (!is_enabled) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | if (!IsDriverValid(data)) { | ||
| 33 | return; | ||
| 34 | } | ||
| 35 | |||
| 36 | switch (input_type) { | ||
| 37 | case Polling::InputType::Button: | ||
| 38 | RegisterButton(data); | ||
| 39 | return; | ||
| 40 | case Polling::InputType::Stick: | ||
| 41 | RegisterStick(data); | ||
| 42 | return; | ||
| 43 | case Polling::InputType::Motion: | ||
| 44 | RegisterMotion(data); | ||
| 45 | return; | ||
| 46 | default: | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | void MappingFactory::StopMapping() { | ||
| 52 | is_enabled = false; | ||
| 53 | input_type = Polling::InputType::None; | ||
| 54 | input_queue.Clear(); | ||
| 55 | } | ||
| 56 | |||
| 57 | void MappingFactory::RegisterButton(const MappingData& data) { | ||
| 58 | Common::ParamPackage new_input; | ||
| 59 | new_input.Set("engine", data.engine); | ||
| 60 | if (data.pad.guid != Common::UUID{}) { | ||
| 61 | new_input.Set("guid", data.pad.guid.Format()); | ||
| 62 | } | ||
| 63 | new_input.Set("port", static_cast<int>(data.pad.port)); | ||
| 64 | new_input.Set("pad", static_cast<int>(data.pad.pad)); | ||
| 65 | |||
| 66 | switch (data.type) { | ||
| 67 | case EngineInputType::Button: | ||
| 68 | // Workaround for old compatibility | ||
| 69 | if (data.engine == "keyboard") { | ||
| 70 | new_input.Set("code", data.index); | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | new_input.Set("button", data.index); | ||
| 74 | break; | ||
| 75 | case EngineInputType::HatButton: | ||
| 76 | new_input.Set("hat", data.index); | ||
| 77 | new_input.Set("direction", data.hat_name); | ||
| 78 | break; | ||
| 79 | case EngineInputType::Analog: | ||
| 80 | // Ignore mouse axis when mapping buttons | ||
| 81 | if (data.engine == "mouse") { | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | new_input.Set("axis", data.index); | ||
| 85 | new_input.Set("threshold", 0.5f); | ||
| 86 | break; | ||
| 87 | default: | ||
| 88 | return; | ||
| 89 | } | ||
| 90 | input_queue.Push(new_input); | ||
| 91 | } | ||
| 92 | |||
| 93 | void MappingFactory::RegisterStick(const MappingData& data) { | ||
| 94 | Common::ParamPackage new_input; | ||
| 95 | new_input.Set("engine", data.engine); | ||
| 96 | if (data.pad.guid != Common::UUID{}) { | ||
| 97 | new_input.Set("guid", data.pad.guid.Format()); | ||
| 98 | } | ||
| 99 | new_input.Set("port", static_cast<int>(data.pad.port)); | ||
| 100 | new_input.Set("pad", static_cast<int>(data.pad.pad)); | ||
| 101 | |||
| 102 | // If engine is mouse map the mouse position as a joystick | ||
| 103 | if (data.engine == "mouse") { | ||
| 104 | new_input.Set("axis_x", 0); | ||
| 105 | new_input.Set("axis_y", 1); | ||
| 106 | new_input.Set("threshold", 0.5f); | ||
| 107 | new_input.Set("range", 1.0f); | ||
| 108 | new_input.Set("deadzone", 0.0f); | ||
| 109 | input_queue.Push(new_input); | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | |||
| 113 | switch (data.type) { | ||
| 114 | case EngineInputType::Button: | ||
| 115 | case EngineInputType::HatButton: | ||
| 116 | RegisterButton(data); | ||
| 117 | return; | ||
| 118 | case EngineInputType::Analog: | ||
| 119 | if (first_axis == data.index) { | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | if (first_axis == -1) { | ||
| 123 | first_axis = data.index; | ||
| 124 | return; | ||
| 125 | } | ||
| 126 | new_input.Set("axis_x", first_axis); | ||
| 127 | new_input.Set("axis_y", data.index); | ||
| 128 | new_input.Set("threshold", 0.5f); | ||
| 129 | new_input.Set("range", 0.95f); | ||
| 130 | new_input.Set("deadzone", 0.15f); | ||
| 131 | break; | ||
| 132 | default: | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | input_queue.Push(new_input); | ||
| 136 | } | ||
| 137 | |||
| 138 | void MappingFactory::RegisterMotion(const MappingData& data) { | ||
| 139 | Common::ParamPackage new_input; | ||
| 140 | new_input.Set("engine", data.engine); | ||
| 141 | if (data.pad.guid != Common::UUID{}) { | ||
| 142 | new_input.Set("guid", data.pad.guid.Format()); | ||
| 143 | } | ||
| 144 | new_input.Set("port", static_cast<int>(data.pad.port)); | ||
| 145 | new_input.Set("pad", static_cast<int>(data.pad.pad)); | ||
| 146 | switch (data.type) { | ||
| 147 | case EngineInputType::Button: | ||
| 148 | case EngineInputType::HatButton: | ||
| 149 | RegisterButton(data); | ||
| 150 | return; | ||
| 151 | case EngineInputType::Analog: | ||
| 152 | if (first_axis == data.index) { | ||
| 153 | return; | ||
| 154 | } | ||
| 155 | if (second_axis == data.index) { | ||
| 156 | return; | ||
| 157 | } | ||
| 158 | if (first_axis == -1) { | ||
| 159 | first_axis = data.index; | ||
| 160 | return; | ||
| 161 | } | ||
| 162 | if (second_axis == -1) { | ||
| 163 | second_axis = data.index; | ||
| 164 | return; | ||
| 165 | } | ||
| 166 | new_input.Set("axis_x", first_axis); | ||
| 167 | new_input.Set("axis_y", second_axis); | ||
| 168 | new_input.Set("axis_z", data.index); | ||
| 169 | new_input.Set("range", 1.0f); | ||
| 170 | new_input.Set("deadzone", 0.20f); | ||
| 171 | break; | ||
| 172 | case EngineInputType::Motion: | ||
| 173 | new_input.Set("motion", data.index); | ||
| 174 | break; | ||
| 175 | default: | ||
| 176 | return; | ||
| 177 | } | ||
| 178 | input_queue.Push(new_input); | ||
| 179 | } | ||
| 180 | |||
| 181 | bool MappingFactory::IsDriverValid(const MappingData& data) const { | ||
| 182 | // Only port 0 can be mapped on the keyboard | ||
| 183 | if (data.engine == "keyboard" && data.pad.port != 0) { | ||
| 184 | return false; | ||
| 185 | } | ||
| 186 | // To prevent mapping with two devices we disable any UDP except motion | ||
| 187 | if (!Settings::values.enable_udp_controller && data.engine == "cemuhookudp" && | ||
| 188 | data.type != EngineInputType::Motion) { | ||
| 189 | return false; | ||
| 190 | } | ||
| 191 | // The following drivers don't need to be mapped | ||
| 192 | if (data.engine == "tas") { | ||
| 193 | return false; | ||
| 194 | } | ||
| 195 | if (data.engine == "touch") { | ||
| 196 | return false; | ||
| 197 | } | ||
| 198 | if (data.engine == "touch_from_button") { | ||
| 199 | return false; | ||
| 200 | } | ||
| 201 | if (data.engine == "analog_from_button") { | ||
| 202 | return false; | ||
| 203 | } | ||
| 204 | return true; | ||
| 205 | } | ||
| 206 | |||
| 207 | } // namespace InputCommon | ||
diff --git a/src/input_common/input_mapping.h b/src/input_common/input_mapping.h new file mode 100644 index 000000000..93564b5f8 --- /dev/null +++ b/src/input_common/input_mapping.h | |||
| @@ -0,0 +1,83 @@ | |||
| 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 | #include "common/threadsafe_queue.h" | ||
| 7 | |||
| 8 | namespace InputCommon { | ||
| 9 | class InputEngine; | ||
| 10 | struct MappingData; | ||
| 11 | |||
| 12 | class MappingFactory { | ||
| 13 | public: | ||
| 14 | MappingFactory(); | ||
| 15 | |||
| 16 | /** | ||
| 17 | * Resets all variables to begin the mapping process | ||
| 18 | * @param type type of input desired to be returned | ||
| 19 | */ | ||
| 20 | void BeginMapping(Polling::InputType type); | ||
| 21 | |||
| 22 | /// Returns an input event with mapping information from the input_queue | ||
| 23 | [[nodiscard]] const Common::ParamPackage GetNextInput(); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Registers mapping input data from the driver | ||
| 27 | * @param data A struct containing all the information needed to create a proper | ||
| 28 | * ParamPackage | ||
| 29 | */ | ||
| 30 | void RegisterInput(const MappingData& data); | ||
| 31 | |||
| 32 | /// Stop polling from all backends | ||
| 33 | void StopMapping(); | ||
| 34 | |||
| 35 | private: | ||
| 36 | /** | ||
| 37 | * If provided data satisfies the requirements it will push an element to the input_queue | ||
| 38 | * Supported input: | ||
| 39 | * - Button: Creates a basic button ParamPackage | ||
| 40 | * - HatButton: Creates a basic hat button ParamPackage | ||
| 41 | * - Analog: Creates a basic analog ParamPackage | ||
| 42 | * @param data A struct containing all the information needed to create a proper | ||
| 43 | * ParamPackage | ||
| 44 | */ | ||
| 45 | void RegisterButton(const MappingData& data); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * If provided data satisfies the requirements it will push an element to the input_queue | ||
| 49 | * Supported input: | ||
| 50 | * - Button, HatButton: Pass the data to RegisterButton | ||
| 51 | * - Analog: Stores the first axis and on the second axis creates a basic stick ParamPackage | ||
| 52 | * @param data A struct containing all the information needed to create a proper | ||
| 53 | * ParamPackage | ||
| 54 | */ | ||
| 55 | void RegisterStick(const MappingData& data); | ||
| 56 | |||
| 57 | /** | ||
| 58 | * If provided data satisfies the requirements it will push an element to the input_queue | ||
| 59 | * Supported input: | ||
| 60 | * - Button, HatButton: Pass the data to RegisterButton | ||
| 61 | * - Analog: Stores the first two axis and on the third axis creates a basic Motion | ||
| 62 | * ParamPackage | ||
| 63 | * - Motion: Creates a basic Motion ParamPackage | ||
| 64 | * @param data A struct containing all the information needed to create a proper | ||
| 65 | * ParamPackage | ||
| 66 | */ | ||
| 67 | void RegisterMotion(const MappingData& data); | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Returns true if driver can be mapped | ||
| 71 | * @param data A struct containing all the information needed to create a proper | ||
| 72 | * ParamPackage | ||
| 73 | */ | ||
| 74 | bool IsDriverValid(const MappingData& data) const; | ||
| 75 | |||
| 76 | Common::SPSCQueue<Common::ParamPackage> input_queue; | ||
| 77 | Polling::InputType input_type{Polling::InputType::None}; | ||
| 78 | bool is_enabled{}; | ||
| 79 | int first_axis = -1; | ||
| 80 | int second_axis = -1; | ||
| 81 | }; | ||
| 82 | |||
| 83 | } // namespace InputCommon | ||
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp new file mode 100644 index 000000000..7b370335f --- /dev/null +++ b/src/input_common/input_poller.cpp | |||
| @@ -0,0 +1,970 @@ | |||
| 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/common_types.h" | ||
| 6 | #include "common/input.h" | ||
| 7 | |||
| 8 | #include "input_common/input_engine.h" | ||
| 9 | #include "input_common/input_poller.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | class DummyInput final : public Common::Input::InputDevice { | ||
| 14 | public: | ||
| 15 | explicit DummyInput() = default; | ||
| 16 | }; | ||
| 17 | |||
| 18 | class InputFromButton final : public Common::Input::InputDevice { | ||
| 19 | public: | ||
| 20 | explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, | ||
| 21 | InputEngine* input_engine_) | ||
| 22 | : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), | ||
| 23 | input_engine(input_engine_) { | ||
| 24 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 25 | const InputIdentifier input_identifier{ | ||
| 26 | .identifier = identifier, | ||
| 27 | .type = EngineInputType::Button, | ||
| 28 | .index = button, | ||
| 29 | .callback = engine_callback, | ||
| 30 | }; | ||
| 31 | last_button_value = false; | ||
| 32 | callback_key = input_engine->SetCallback(input_identifier); | ||
| 33 | } | ||
| 34 | |||
| 35 | ~InputFromButton() override { | ||
| 36 | input_engine->DeleteCallback(callback_key); | ||
| 37 | } | ||
| 38 | |||
| 39 | Common::Input::ButtonStatus GetStatus() const { | ||
| 40 | return { | ||
| 41 | .value = input_engine->GetButton(identifier, button), | ||
| 42 | .inverted = inverted, | ||
| 43 | .toggle = toggle, | ||
| 44 | }; | ||
| 45 | } | ||
| 46 | |||
| 47 | void ForceUpdate() override { | ||
| 48 | const Common::Input::CallbackStatus status{ | ||
| 49 | .type = Common::Input::InputType::Button, | ||
| 50 | .button_status = GetStatus(), | ||
| 51 | }; | ||
| 52 | |||
| 53 | last_button_value = status.button_status.value; | ||
| 54 | TriggerOnChange(status); | ||
| 55 | } | ||
| 56 | |||
| 57 | void OnChange() { | ||
| 58 | const Common::Input::CallbackStatus status{ | ||
| 59 | .type = Common::Input::InputType::Button, | ||
| 60 | .button_status = GetStatus(), | ||
| 61 | }; | ||
| 62 | |||
| 63 | if (status.button_status.value != last_button_value) { | ||
| 64 | last_button_value = status.button_status.value; | ||
| 65 | TriggerOnChange(status); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | private: | ||
| 70 | const PadIdentifier identifier; | ||
| 71 | const int button; | ||
| 72 | const bool toggle; | ||
| 73 | const bool inverted; | ||
| 74 | int callback_key; | ||
| 75 | bool last_button_value; | ||
| 76 | InputEngine* input_engine; | ||
| 77 | }; | ||
| 78 | |||
| 79 | class InputFromHatButton final : public Common::Input::InputDevice { | ||
| 80 | public: | ||
| 81 | explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_, | ||
| 82 | bool inverted_, InputEngine* input_engine_) | ||
| 83 | : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_), | ||
| 84 | inverted(inverted_), input_engine(input_engine_) { | ||
| 85 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 86 | const InputIdentifier input_identifier{ | ||
| 87 | .identifier = identifier, | ||
| 88 | .type = EngineInputType::HatButton, | ||
| 89 | .index = button, | ||
| 90 | .callback = engine_callback, | ||
| 91 | }; | ||
| 92 | last_button_value = false; | ||
| 93 | callback_key = input_engine->SetCallback(input_identifier); | ||
| 94 | } | ||
| 95 | |||
| 96 | ~InputFromHatButton() override { | ||
| 97 | input_engine->DeleteCallback(callback_key); | ||
| 98 | } | ||
| 99 | |||
| 100 | Common::Input::ButtonStatus GetStatus() const { | ||
| 101 | return { | ||
| 102 | .value = input_engine->GetHatButton(identifier, button, direction), | ||
| 103 | .inverted = inverted, | ||
| 104 | .toggle = toggle, | ||
| 105 | }; | ||
| 106 | } | ||
| 107 | |||
| 108 | void ForceUpdate() override { | ||
| 109 | const Common::Input::CallbackStatus status{ | ||
| 110 | .type = Common::Input::InputType::Button, | ||
| 111 | .button_status = GetStatus(), | ||
| 112 | }; | ||
| 113 | |||
| 114 | last_button_value = status.button_status.value; | ||
| 115 | TriggerOnChange(status); | ||
| 116 | } | ||
| 117 | |||
| 118 | void OnChange() { | ||
| 119 | const Common::Input::CallbackStatus status{ | ||
| 120 | .type = Common::Input::InputType::Button, | ||
| 121 | .button_status = GetStatus(), | ||
| 122 | }; | ||
| 123 | |||
| 124 | if (status.button_status.value != last_button_value) { | ||
| 125 | last_button_value = status.button_status.value; | ||
| 126 | TriggerOnChange(status); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | private: | ||
| 131 | const PadIdentifier identifier; | ||
| 132 | const int button; | ||
| 133 | const u8 direction; | ||
| 134 | const bool toggle; | ||
| 135 | const bool inverted; | ||
| 136 | int callback_key; | ||
| 137 | bool last_button_value; | ||
| 138 | InputEngine* input_engine; | ||
| 139 | }; | ||
| 140 | |||
| 141 | class InputFromStick final : public Common::Input::InputDevice { | ||
| 142 | public: | ||
| 143 | explicit InputFromStick(PadIdentifier identifier_, int axis_x_, int axis_y_, | ||
| 144 | Common::Input::AnalogProperties properties_x_, | ||
| 145 | Common::Input::AnalogProperties properties_y_, | ||
| 146 | InputEngine* input_engine_) | ||
| 147 | : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), | ||
| 148 | properties_y(properties_y_), | ||
| 149 | input_engine(input_engine_), invert_axis_y{input_engine_->GetEngineName() == "sdl"} { | ||
| 150 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 151 | const InputIdentifier x_input_identifier{ | ||
| 152 | .identifier = identifier, | ||
| 153 | .type = EngineInputType::Analog, | ||
| 154 | .index = axis_x, | ||
| 155 | .callback = engine_callback, | ||
| 156 | }; | ||
| 157 | const InputIdentifier y_input_identifier{ | ||
| 158 | .identifier = identifier, | ||
| 159 | .type = EngineInputType::Analog, | ||
| 160 | .index = axis_y, | ||
| 161 | .callback = engine_callback, | ||
| 162 | }; | ||
| 163 | last_axis_x_value = 0.0f; | ||
| 164 | last_axis_y_value = 0.0f; | ||
| 165 | callback_key_x = input_engine->SetCallback(x_input_identifier); | ||
| 166 | callback_key_y = input_engine->SetCallback(y_input_identifier); | ||
| 167 | } | ||
| 168 | |||
| 169 | ~InputFromStick() override { | ||
| 170 | input_engine->DeleteCallback(callback_key_x); | ||
| 171 | input_engine->DeleteCallback(callback_key_y); | ||
| 172 | } | ||
| 173 | |||
| 174 | Common::Input::StickStatus GetStatus() const { | ||
| 175 | Common::Input::StickStatus status; | ||
| 176 | status.x = { | ||
| 177 | .raw_value = input_engine->GetAxis(identifier, axis_x), | ||
| 178 | .properties = properties_x, | ||
| 179 | }; | ||
| 180 | status.y = { | ||
| 181 | .raw_value = input_engine->GetAxis(identifier, axis_y), | ||
| 182 | .properties = properties_y, | ||
| 183 | }; | ||
| 184 | // This is a workaround too keep compatibility with old yuzu versions. Vertical axis is | ||
| 185 | // inverted on SDL compared to Nintendo | ||
| 186 | if (invert_axis_y) { | ||
| 187 | status.y.raw_value = -status.y.raw_value; | ||
| 188 | } | ||
| 189 | return status; | ||
| 190 | } | ||
| 191 | |||
| 192 | void ForceUpdate() override { | ||
| 193 | const Common::Input::CallbackStatus status{ | ||
| 194 | .type = Common::Input::InputType::Stick, | ||
| 195 | .stick_status = GetStatus(), | ||
| 196 | }; | ||
| 197 | |||
| 198 | last_axis_x_value = status.stick_status.x.raw_value; | ||
| 199 | last_axis_y_value = status.stick_status.y.raw_value; | ||
| 200 | TriggerOnChange(status); | ||
| 201 | } | ||
| 202 | |||
| 203 | void OnChange() { | ||
| 204 | const Common::Input::CallbackStatus status{ | ||
| 205 | .type = Common::Input::InputType::Stick, | ||
| 206 | .stick_status = GetStatus(), | ||
| 207 | }; | ||
| 208 | |||
| 209 | if (status.stick_status.x.raw_value != last_axis_x_value || | ||
| 210 | status.stick_status.y.raw_value != last_axis_y_value) { | ||
| 211 | last_axis_x_value = status.stick_status.x.raw_value; | ||
| 212 | last_axis_y_value = status.stick_status.y.raw_value; | ||
| 213 | TriggerOnChange(status); | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | private: | ||
| 218 | const PadIdentifier identifier; | ||
| 219 | const int axis_x; | ||
| 220 | const int axis_y; | ||
| 221 | const Common::Input::AnalogProperties properties_x; | ||
| 222 | const Common::Input::AnalogProperties properties_y; | ||
| 223 | int callback_key_x; | ||
| 224 | int callback_key_y; | ||
| 225 | float last_axis_x_value; | ||
| 226 | float last_axis_y_value; | ||
| 227 | InputEngine* input_engine; | ||
| 228 | const bool invert_axis_y; | ||
| 229 | }; | ||
| 230 | |||
| 231 | class InputFromTouch final : public Common::Input::InputDevice { | ||
| 232 | public: | ||
| 233 | explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_, | ||
| 234 | bool inverted_, int axis_x_, int axis_y_, | ||
| 235 | Common::Input::AnalogProperties properties_x_, | ||
| 236 | Common::Input::AnalogProperties properties_y_, | ||
| 237 | InputEngine* input_engine_) | ||
| 238 | : identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_), | ||
| 239 | inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), | ||
| 240 | properties_y(properties_y_), input_engine(input_engine_) { | ||
| 241 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 242 | const InputIdentifier button_input_identifier{ | ||
| 243 | .identifier = identifier, | ||
| 244 | .type = EngineInputType::Button, | ||
| 245 | .index = button, | ||
| 246 | .callback = engine_callback, | ||
| 247 | }; | ||
| 248 | const InputIdentifier x_input_identifier{ | ||
| 249 | .identifier = identifier, | ||
| 250 | .type = EngineInputType::Analog, | ||
| 251 | .index = axis_x, | ||
| 252 | .callback = engine_callback, | ||
| 253 | }; | ||
| 254 | const InputIdentifier y_input_identifier{ | ||
| 255 | .identifier = identifier, | ||
| 256 | .type = EngineInputType::Analog, | ||
| 257 | .index = axis_y, | ||
| 258 | .callback = engine_callback, | ||
| 259 | }; | ||
| 260 | last_axis_x_value = 0.0f; | ||
| 261 | last_axis_y_value = 0.0f; | ||
| 262 | last_button_value = false; | ||
| 263 | callback_key_button = input_engine->SetCallback(button_input_identifier); | ||
| 264 | callback_key_x = input_engine->SetCallback(x_input_identifier); | ||
| 265 | callback_key_y = input_engine->SetCallback(y_input_identifier); | ||
| 266 | } | ||
| 267 | |||
| 268 | ~InputFromTouch() override { | ||
| 269 | input_engine->DeleteCallback(callback_key_button); | ||
| 270 | input_engine->DeleteCallback(callback_key_x); | ||
| 271 | input_engine->DeleteCallback(callback_key_y); | ||
| 272 | } | ||
| 273 | |||
| 274 | Common::Input::TouchStatus GetStatus() const { | ||
| 275 | Common::Input::TouchStatus status; | ||
| 276 | status.id = touch_id; | ||
| 277 | status.pressed = { | ||
| 278 | .value = input_engine->GetButton(identifier, button), | ||
| 279 | .inverted = inverted, | ||
| 280 | .toggle = toggle, | ||
| 281 | }; | ||
| 282 | status.x = { | ||
| 283 | .raw_value = input_engine->GetAxis(identifier, axis_x), | ||
| 284 | .properties = properties_x, | ||
| 285 | }; | ||
| 286 | status.y = { | ||
| 287 | .raw_value = input_engine->GetAxis(identifier, axis_y), | ||
| 288 | .properties = properties_y, | ||
| 289 | }; | ||
| 290 | return status; | ||
| 291 | } | ||
| 292 | |||
| 293 | void OnChange() { | ||
| 294 | const Common::Input::CallbackStatus status{ | ||
| 295 | .type = Common::Input::InputType::Touch, | ||
| 296 | .touch_status = GetStatus(), | ||
| 297 | }; | ||
| 298 | |||
| 299 | if (status.touch_status.x.raw_value != last_axis_x_value || | ||
| 300 | status.touch_status.y.raw_value != last_axis_y_value || | ||
| 301 | status.touch_status.pressed.value != last_button_value) { | ||
| 302 | last_axis_x_value = status.touch_status.x.raw_value; | ||
| 303 | last_axis_y_value = status.touch_status.y.raw_value; | ||
| 304 | last_button_value = status.touch_status.pressed.value; | ||
| 305 | TriggerOnChange(status); | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | private: | ||
| 310 | const PadIdentifier identifier; | ||
| 311 | const int touch_id; | ||
| 312 | const int button; | ||
| 313 | const bool toggle; | ||
| 314 | const bool inverted; | ||
| 315 | const int axis_x; | ||
| 316 | const int axis_y; | ||
| 317 | const Common::Input::AnalogProperties properties_x; | ||
| 318 | const Common::Input::AnalogProperties properties_y; | ||
| 319 | int callback_key_button; | ||
| 320 | int callback_key_x; | ||
| 321 | int callback_key_y; | ||
| 322 | bool last_button_value; | ||
| 323 | float last_axis_x_value; | ||
| 324 | float last_axis_y_value; | ||
| 325 | InputEngine* input_engine; | ||
| 326 | }; | ||
| 327 | |||
| 328 | class InputFromTrigger final : public Common::Input::InputDevice { | ||
| 329 | public: | ||
| 330 | explicit InputFromTrigger(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, | ||
| 331 | int axis_, Common::Input::AnalogProperties properties_, | ||
| 332 | InputEngine* input_engine_) | ||
| 333 | : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), | ||
| 334 | axis(axis_), properties(properties_), input_engine(input_engine_) { | ||
| 335 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 336 | const InputIdentifier button_input_identifier{ | ||
| 337 | .identifier = identifier, | ||
| 338 | .type = EngineInputType::Button, | ||
| 339 | .index = button, | ||
| 340 | .callback = engine_callback, | ||
| 341 | }; | ||
| 342 | const InputIdentifier axis_input_identifier{ | ||
| 343 | .identifier = identifier, | ||
| 344 | .type = EngineInputType::Analog, | ||
| 345 | .index = axis, | ||
| 346 | .callback = engine_callback, | ||
| 347 | }; | ||
| 348 | last_axis_value = 0.0f; | ||
| 349 | last_button_value = false; | ||
| 350 | callback_key_button = input_engine->SetCallback(button_input_identifier); | ||
| 351 | axis_callback_key = input_engine->SetCallback(axis_input_identifier); | ||
| 352 | } | ||
| 353 | |||
| 354 | ~InputFromTrigger() override { | ||
| 355 | input_engine->DeleteCallback(callback_key_button); | ||
| 356 | input_engine->DeleteCallback(axis_callback_key); | ||
| 357 | } | ||
| 358 | |||
| 359 | Common::Input::TriggerStatus GetStatus() const { | ||
| 360 | const Common::Input::AnalogStatus analog_status{ | ||
| 361 | .raw_value = input_engine->GetAxis(identifier, axis), | ||
| 362 | .properties = properties, | ||
| 363 | }; | ||
| 364 | const Common::Input::ButtonStatus button_status{ | ||
| 365 | .value = input_engine->GetButton(identifier, button), | ||
| 366 | .inverted = inverted, | ||
| 367 | .toggle = toggle, | ||
| 368 | }; | ||
| 369 | return { | ||
| 370 | .analog = analog_status, | ||
| 371 | .pressed = button_status, | ||
| 372 | }; | ||
| 373 | } | ||
| 374 | |||
| 375 | void OnChange() { | ||
| 376 | const Common::Input::CallbackStatus status{ | ||
| 377 | .type = Common::Input::InputType::Trigger, | ||
| 378 | .trigger_status = GetStatus(), | ||
| 379 | }; | ||
| 380 | |||
| 381 | if (status.trigger_status.analog.raw_value != last_axis_value || | ||
| 382 | status.trigger_status.pressed.value != last_button_value) { | ||
| 383 | last_axis_value = status.trigger_status.analog.raw_value; | ||
| 384 | last_button_value = status.trigger_status.pressed.value; | ||
| 385 | TriggerOnChange(status); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 389 | private: | ||
| 390 | const PadIdentifier identifier; | ||
| 391 | const int button; | ||
| 392 | const bool toggle; | ||
| 393 | const bool inverted; | ||
| 394 | const int axis; | ||
| 395 | const Common::Input::AnalogProperties properties; | ||
| 396 | int callback_key_button; | ||
| 397 | int axis_callback_key; | ||
| 398 | bool last_button_value; | ||
| 399 | float last_axis_value; | ||
| 400 | InputEngine* input_engine; | ||
| 401 | }; | ||
| 402 | |||
| 403 | class InputFromAnalog final : public Common::Input::InputDevice { | ||
| 404 | public: | ||
| 405 | explicit InputFromAnalog(PadIdentifier identifier_, int axis_, | ||
| 406 | Common::Input::AnalogProperties properties_, | ||
| 407 | InputEngine* input_engine_) | ||
| 408 | : identifier(identifier_), axis(axis_), properties(properties_), | ||
| 409 | input_engine(input_engine_) { | ||
| 410 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 411 | const InputIdentifier input_identifier{ | ||
| 412 | .identifier = identifier, | ||
| 413 | .type = EngineInputType::Analog, | ||
| 414 | .index = axis, | ||
| 415 | .callback = engine_callback, | ||
| 416 | }; | ||
| 417 | last_axis_value = 0.0f; | ||
| 418 | callback_key = input_engine->SetCallback(input_identifier); | ||
| 419 | } | ||
| 420 | |||
| 421 | ~InputFromAnalog() override { | ||
| 422 | input_engine->DeleteCallback(callback_key); | ||
| 423 | } | ||
| 424 | |||
| 425 | Common::Input::AnalogStatus GetStatus() const { | ||
| 426 | return { | ||
| 427 | .raw_value = input_engine->GetAxis(identifier, axis), | ||
| 428 | .properties = properties, | ||
| 429 | }; | ||
| 430 | } | ||
| 431 | |||
| 432 | void OnChange() { | ||
| 433 | const Common::Input::CallbackStatus status{ | ||
| 434 | .type = Common::Input::InputType::Analog, | ||
| 435 | .analog_status = GetStatus(), | ||
| 436 | }; | ||
| 437 | |||
| 438 | if (status.analog_status.raw_value != last_axis_value) { | ||
| 439 | last_axis_value = status.analog_status.raw_value; | ||
| 440 | TriggerOnChange(status); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | private: | ||
| 445 | const PadIdentifier identifier; | ||
| 446 | const int axis; | ||
| 447 | const Common::Input::AnalogProperties properties; | ||
| 448 | int callback_key; | ||
| 449 | float last_axis_value; | ||
| 450 | InputEngine* input_engine; | ||
| 451 | }; | ||
| 452 | |||
| 453 | class InputFromBattery final : public Common::Input::InputDevice { | ||
| 454 | public: | ||
| 455 | explicit InputFromBattery(PadIdentifier identifier_, InputEngine* input_engine_) | ||
| 456 | : identifier(identifier_), input_engine(input_engine_) { | ||
| 457 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 458 | const InputIdentifier input_identifier{ | ||
| 459 | .identifier = identifier, | ||
| 460 | .type = EngineInputType::Battery, | ||
| 461 | .index = 0, | ||
| 462 | .callback = engine_callback, | ||
| 463 | }; | ||
| 464 | last_battery_value = Common::Input::BatteryStatus::Charging; | ||
| 465 | callback_key = input_engine->SetCallback(input_identifier); | ||
| 466 | } | ||
| 467 | |||
| 468 | ~InputFromBattery() override { | ||
| 469 | input_engine->DeleteCallback(callback_key); | ||
| 470 | } | ||
| 471 | |||
| 472 | Common::Input::BatteryStatus GetStatus() const { | ||
| 473 | return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier)); | ||
| 474 | } | ||
| 475 | |||
| 476 | void ForceUpdate() override { | ||
| 477 | const Common::Input::CallbackStatus status{ | ||
| 478 | .type = Common::Input::InputType::Battery, | ||
| 479 | .battery_status = GetStatus(), | ||
| 480 | }; | ||
| 481 | |||
| 482 | last_battery_value = status.battery_status; | ||
| 483 | TriggerOnChange(status); | ||
| 484 | } | ||
| 485 | |||
| 486 | void OnChange() { | ||
| 487 | const Common::Input::CallbackStatus status{ | ||
| 488 | .type = Common::Input::InputType::Battery, | ||
| 489 | .battery_status = GetStatus(), | ||
| 490 | }; | ||
| 491 | |||
| 492 | if (status.battery_status != last_battery_value) { | ||
| 493 | last_battery_value = status.battery_status; | ||
| 494 | TriggerOnChange(status); | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | private: | ||
| 499 | const PadIdentifier identifier; | ||
| 500 | int callback_key; | ||
| 501 | Common::Input::BatteryStatus last_battery_value; | ||
| 502 | InputEngine* input_engine; | ||
| 503 | }; | ||
| 504 | |||
| 505 | class InputFromMotion final : public Common::Input::InputDevice { | ||
| 506 | public: | ||
| 507 | explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, | ||
| 508 | InputEngine* input_engine_) | ||
| 509 | : identifier(identifier_), motion_sensor(motion_sensor_), input_engine(input_engine_) { | ||
| 510 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 511 | const InputIdentifier input_identifier{ | ||
| 512 | .identifier = identifier, | ||
| 513 | .type = EngineInputType::Motion, | ||
| 514 | .index = motion_sensor, | ||
| 515 | .callback = engine_callback, | ||
| 516 | }; | ||
| 517 | callback_key = input_engine->SetCallback(input_identifier); | ||
| 518 | } | ||
| 519 | |||
| 520 | ~InputFromMotion() override { | ||
| 521 | input_engine->DeleteCallback(callback_key); | ||
| 522 | } | ||
| 523 | |||
| 524 | Common::Input::MotionStatus GetStatus() const { | ||
| 525 | const auto basic_motion = input_engine->GetMotion(identifier, motion_sensor); | ||
| 526 | Common::Input::MotionStatus status{}; | ||
| 527 | const Common::Input::AnalogProperties properties = { | ||
| 528 | .deadzone = 0.001f, | ||
| 529 | .range = 1.0f, | ||
| 530 | .offset = 0.0f, | ||
| 531 | }; | ||
| 532 | status.accel.x = {.raw_value = basic_motion.accel_x, .properties = properties}; | ||
| 533 | status.accel.y = {.raw_value = basic_motion.accel_y, .properties = properties}; | ||
| 534 | status.accel.z = {.raw_value = basic_motion.accel_z, .properties = properties}; | ||
| 535 | status.gyro.x = {.raw_value = basic_motion.gyro_x, .properties = properties}; | ||
| 536 | status.gyro.y = {.raw_value = basic_motion.gyro_y, .properties = properties}; | ||
| 537 | status.gyro.z = {.raw_value = basic_motion.gyro_z, .properties = properties}; | ||
| 538 | status.delta_timestamp = basic_motion.delta_timestamp; | ||
| 539 | return status; | ||
| 540 | } | ||
| 541 | |||
| 542 | void OnChange() { | ||
| 543 | const Common::Input::CallbackStatus status{ | ||
| 544 | .type = Common::Input::InputType::Motion, | ||
| 545 | .motion_status = GetStatus(), | ||
| 546 | }; | ||
| 547 | |||
| 548 | TriggerOnChange(status); | ||
| 549 | } | ||
| 550 | |||
| 551 | private: | ||
| 552 | const PadIdentifier identifier; | ||
| 553 | const int motion_sensor; | ||
| 554 | int callback_key; | ||
| 555 | InputEngine* input_engine; | ||
| 556 | }; | ||
| 557 | |||
| 558 | class InputFromAxisMotion final : public Common::Input::InputDevice { | ||
| 559 | public: | ||
| 560 | explicit InputFromAxisMotion(PadIdentifier identifier_, int axis_x_, int axis_y_, int axis_z_, | ||
| 561 | Common::Input::AnalogProperties properties_x_, | ||
| 562 | Common::Input::AnalogProperties properties_y_, | ||
| 563 | Common::Input::AnalogProperties properties_z_, | ||
| 564 | InputEngine* input_engine_) | ||
| 565 | : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), axis_z(axis_z_), | ||
| 566 | properties_x(properties_x_), properties_y(properties_y_), properties_z(properties_z_), | ||
| 567 | input_engine(input_engine_) { | ||
| 568 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 569 | const InputIdentifier x_input_identifier{ | ||
| 570 | .identifier = identifier, | ||
| 571 | .type = EngineInputType::Analog, | ||
| 572 | .index = axis_x, | ||
| 573 | .callback = engine_callback, | ||
| 574 | }; | ||
| 575 | const InputIdentifier y_input_identifier{ | ||
| 576 | .identifier = identifier, | ||
| 577 | .type = EngineInputType::Analog, | ||
| 578 | .index = axis_y, | ||
| 579 | .callback = engine_callback, | ||
| 580 | }; | ||
| 581 | const InputIdentifier z_input_identifier{ | ||
| 582 | .identifier = identifier, | ||
| 583 | .type = EngineInputType::Analog, | ||
| 584 | .index = axis_z, | ||
| 585 | .callback = engine_callback, | ||
| 586 | }; | ||
| 587 | last_axis_x_value = 0.0f; | ||
| 588 | last_axis_y_value = 0.0f; | ||
| 589 | last_axis_z_value = 0.0f; | ||
| 590 | callback_key_x = input_engine->SetCallback(x_input_identifier); | ||
| 591 | callback_key_y = input_engine->SetCallback(y_input_identifier); | ||
| 592 | callback_key_z = input_engine->SetCallback(z_input_identifier); | ||
| 593 | } | ||
| 594 | |||
| 595 | ~InputFromAxisMotion() override { | ||
| 596 | input_engine->DeleteCallback(callback_key_x); | ||
| 597 | input_engine->DeleteCallback(callback_key_y); | ||
| 598 | input_engine->DeleteCallback(callback_key_z); | ||
| 599 | } | ||
| 600 | |||
| 601 | Common::Input::MotionStatus GetStatus() const { | ||
| 602 | Common::Input::MotionStatus status{}; | ||
| 603 | status.gyro.x = { | ||
| 604 | .raw_value = input_engine->GetAxis(identifier, axis_x), | ||
| 605 | .properties = properties_x, | ||
| 606 | }; | ||
| 607 | status.gyro.y = { | ||
| 608 | .raw_value = input_engine->GetAxis(identifier, axis_y), | ||
| 609 | .properties = properties_y, | ||
| 610 | }; | ||
| 611 | status.gyro.z = { | ||
| 612 | .raw_value = input_engine->GetAxis(identifier, axis_z), | ||
| 613 | .properties = properties_z, | ||
| 614 | }; | ||
| 615 | status.delta_timestamp = 5000; | ||
| 616 | status.force_update = true; | ||
| 617 | return status; | ||
| 618 | } | ||
| 619 | |||
| 620 | void ForceUpdate() override { | ||
| 621 | const Common::Input::CallbackStatus status{ | ||
| 622 | .type = Common::Input::InputType::Motion, | ||
| 623 | .motion_status = GetStatus(), | ||
| 624 | }; | ||
| 625 | |||
| 626 | last_axis_x_value = status.motion_status.gyro.x.raw_value; | ||
| 627 | last_axis_y_value = status.motion_status.gyro.y.raw_value; | ||
| 628 | last_axis_z_value = status.motion_status.gyro.z.raw_value; | ||
| 629 | TriggerOnChange(status); | ||
| 630 | } | ||
| 631 | |||
| 632 | void OnChange() { | ||
| 633 | const Common::Input::CallbackStatus status{ | ||
| 634 | .type = Common::Input::InputType::Motion, | ||
| 635 | .motion_status = GetStatus(), | ||
| 636 | }; | ||
| 637 | |||
| 638 | if (status.motion_status.gyro.x.raw_value != last_axis_x_value || | ||
| 639 | status.motion_status.gyro.y.raw_value != last_axis_y_value || | ||
| 640 | status.motion_status.gyro.z.raw_value != last_axis_z_value) { | ||
| 641 | last_axis_x_value = status.motion_status.gyro.x.raw_value; | ||
| 642 | last_axis_y_value = status.motion_status.gyro.y.raw_value; | ||
| 643 | last_axis_z_value = status.motion_status.gyro.z.raw_value; | ||
| 644 | TriggerOnChange(status); | ||
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 | private: | ||
| 649 | const PadIdentifier identifier; | ||
| 650 | const int axis_x; | ||
| 651 | const int axis_y; | ||
| 652 | const int axis_z; | ||
| 653 | const Common::Input::AnalogProperties properties_x; | ||
| 654 | const Common::Input::AnalogProperties properties_y; | ||
| 655 | const Common::Input::AnalogProperties properties_z; | ||
| 656 | int callback_key_x; | ||
| 657 | int callback_key_y; | ||
| 658 | int callback_key_z; | ||
| 659 | float last_axis_x_value; | ||
| 660 | float last_axis_y_value; | ||
| 661 | float last_axis_z_value; | ||
| 662 | InputEngine* input_engine; | ||
| 663 | }; | ||
| 664 | |||
| 665 | class OutputFromIdentifier final : public Common::Input::OutputDevice { | ||
| 666 | public: | ||
| 667 | explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) | ||
| 668 | : identifier(identifier_), input_engine(input_engine_) {} | ||
| 669 | |||
| 670 | void SetLED(const Common::Input::LedStatus& led_status) override { | ||
| 671 | input_engine->SetLeds(identifier, led_status); | ||
| 672 | } | ||
| 673 | |||
| 674 | Common::Input::VibrationError SetVibration( | ||
| 675 | const Common::Input::VibrationStatus& vibration_status) override { | ||
| 676 | return input_engine->SetRumble(identifier, vibration_status); | ||
| 677 | } | ||
| 678 | |||
| 679 | Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { | ||
| 680 | return input_engine->SetPollingMode(identifier, polling_mode); | ||
| 681 | } | ||
| 682 | |||
| 683 | private: | ||
| 684 | const PadIdentifier identifier; | ||
| 685 | InputEngine* input_engine; | ||
| 686 | }; | ||
| 687 | |||
| 688 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice( | ||
| 689 | const Common::ParamPackage& params) { | ||
| 690 | const PadIdentifier identifier = { | ||
| 691 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 692 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 693 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 694 | }; | ||
| 695 | |||
| 696 | const auto button_id = params.Get("button", 0); | ||
| 697 | const auto keyboard_key = params.Get("code", 0); | ||
| 698 | const auto toggle = params.Get("toggle", false); | ||
| 699 | const auto inverted = params.Get("inverted", false); | ||
| 700 | input_engine->PreSetController(identifier); | ||
| 701 | input_engine->PreSetButton(identifier, button_id); | ||
| 702 | input_engine->PreSetButton(identifier, keyboard_key); | ||
| 703 | if (keyboard_key != 0) { | ||
| 704 | return std::make_unique<InputFromButton>(identifier, keyboard_key, toggle, inverted, | ||
| 705 | input_engine.get()); | ||
| 706 | } | ||
| 707 | return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted, | ||
| 708 | input_engine.get()); | ||
| 709 | } | ||
| 710 | |||
| 711 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice( | ||
| 712 | const Common::ParamPackage& params) { | ||
| 713 | const PadIdentifier identifier = { | ||
| 714 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 715 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 716 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 717 | }; | ||
| 718 | |||
| 719 | const auto button_id = params.Get("hat", 0); | ||
| 720 | const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); | ||
| 721 | const auto toggle = params.Get("toggle", false); | ||
| 722 | const auto inverted = params.Get("inverted", false); | ||
| 723 | |||
| 724 | input_engine->PreSetController(identifier); | ||
| 725 | input_engine->PreSetHatButton(identifier, button_id); | ||
| 726 | return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted, | ||
| 727 | input_engine.get()); | ||
| 728 | } | ||
| 729 | |||
| 730 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice( | ||
| 731 | const Common::ParamPackage& params) { | ||
| 732 | const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f); | ||
| 733 | const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); | ||
| 734 | const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); | ||
| 735 | const PadIdentifier identifier = { | ||
| 736 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 737 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 738 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 739 | }; | ||
| 740 | |||
| 741 | const auto axis_x = params.Get("axis_x", 0); | ||
| 742 | const Common::Input::AnalogProperties properties_x = { | ||
| 743 | .deadzone = deadzone, | ||
| 744 | .range = range, | ||
| 745 | .threshold = threshold, | ||
| 746 | .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f), | ||
| 747 | .inverted = params.Get("invert_x", "+") == "-", | ||
| 748 | }; | ||
| 749 | |||
| 750 | const auto axis_y = params.Get("axis_y", 1); | ||
| 751 | const Common::Input::AnalogProperties properties_y = { | ||
| 752 | .deadzone = deadzone, | ||
| 753 | .range = range, | ||
| 754 | .threshold = threshold, | ||
| 755 | .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f), | ||
| 756 | .inverted = params.Get("invert_y", "+") != "+", | ||
| 757 | }; | ||
| 758 | input_engine->PreSetController(identifier); | ||
| 759 | input_engine->PreSetAxis(identifier, axis_x); | ||
| 760 | input_engine->PreSetAxis(identifier, axis_y); | ||
| 761 | return std::make_unique<InputFromStick>(identifier, axis_x, axis_y, properties_x, properties_y, | ||
| 762 | input_engine.get()); | ||
| 763 | } | ||
| 764 | |||
| 765 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice( | ||
| 766 | const Common::ParamPackage& params) { | ||
| 767 | const PadIdentifier identifier = { | ||
| 768 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 769 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 770 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 771 | }; | ||
| 772 | |||
| 773 | const auto axis = params.Get("axis", 0); | ||
| 774 | const Common::Input::AnalogProperties properties = { | ||
| 775 | .deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f), | ||
| 776 | .range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f), | ||
| 777 | .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), | ||
| 778 | .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), | ||
| 779 | .inverted = params.Get("invert", "+") == "-", | ||
| 780 | }; | ||
| 781 | input_engine->PreSetController(identifier); | ||
| 782 | input_engine->PreSetAxis(identifier, axis); | ||
| 783 | return std::make_unique<InputFromAnalog>(identifier, axis, properties, input_engine.get()); | ||
| 784 | } | ||
| 785 | |||
| 786 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice( | ||
| 787 | const Common::ParamPackage& params) { | ||
| 788 | const PadIdentifier identifier = { | ||
| 789 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 790 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 791 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 792 | }; | ||
| 793 | |||
| 794 | const auto button = params.Get("button", 0); | ||
| 795 | const auto toggle = params.Get("toggle", false); | ||
| 796 | const auto inverted = params.Get("inverted", false); | ||
| 797 | |||
| 798 | const auto axis = params.Get("axis", 0); | ||
| 799 | const Common::Input::AnalogProperties properties = { | ||
| 800 | .deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f), | ||
| 801 | .range = std::clamp(params.Get("range", 1.0f), 0.25f, 2.50f), | ||
| 802 | .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), | ||
| 803 | .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), | ||
| 804 | .inverted = params.Get("invert", false) != 0, | ||
| 805 | }; | ||
| 806 | input_engine->PreSetController(identifier); | ||
| 807 | input_engine->PreSetAxis(identifier, axis); | ||
| 808 | input_engine->PreSetButton(identifier, button); | ||
| 809 | return std::make_unique<InputFromTrigger>(identifier, button, toggle, inverted, axis, | ||
| 810 | properties, input_engine.get()); | ||
| 811 | } | ||
| 812 | |||
| 813 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice( | ||
| 814 | const Common::ParamPackage& params) { | ||
| 815 | const auto touch_id = params.Get("touch_id", 0); | ||
| 816 | const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); | ||
| 817 | const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); | ||
| 818 | const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); | ||
| 819 | const PadIdentifier identifier = { | ||
| 820 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 821 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 822 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 823 | }; | ||
| 824 | |||
| 825 | const auto button = params.Get("button", 0); | ||
| 826 | const auto toggle = params.Get("toggle", false); | ||
| 827 | const auto inverted = params.Get("inverted", false); | ||
| 828 | |||
| 829 | const auto axis_x = params.Get("axis_x", 0); | ||
| 830 | const Common::Input::AnalogProperties properties_x = { | ||
| 831 | .deadzone = deadzone, | ||
| 832 | .range = range, | ||
| 833 | .threshold = threshold, | ||
| 834 | .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f), | ||
| 835 | .inverted = params.Get("invert_x", "+") == "-", | ||
| 836 | }; | ||
| 837 | |||
| 838 | const auto axis_y = params.Get("axis_y", 1); | ||
| 839 | const Common::Input::AnalogProperties properties_y = { | ||
| 840 | .deadzone = deadzone, | ||
| 841 | .range = range, | ||
| 842 | .threshold = threshold, | ||
| 843 | .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f), | ||
| 844 | .inverted = params.Get("invert_y", false) != 0, | ||
| 845 | }; | ||
| 846 | input_engine->PreSetController(identifier); | ||
| 847 | input_engine->PreSetAxis(identifier, axis_x); | ||
| 848 | input_engine->PreSetAxis(identifier, axis_y); | ||
| 849 | input_engine->PreSetButton(identifier, button); | ||
| 850 | return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x, | ||
| 851 | axis_y, properties_x, properties_y, input_engine.get()); | ||
| 852 | } | ||
| 853 | |||
| 854 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice( | ||
| 855 | const Common::ParamPackage& params) { | ||
| 856 | const PadIdentifier identifier = { | ||
| 857 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 858 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 859 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 860 | }; | ||
| 861 | |||
| 862 | input_engine->PreSetController(identifier); | ||
| 863 | return std::make_unique<InputFromBattery>(identifier, input_engine.get()); | ||
| 864 | } | ||
| 865 | |||
| 866 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( | ||
| 867 | Common::ParamPackage params) { | ||
| 868 | const PadIdentifier identifier = { | ||
| 869 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 870 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 871 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 872 | }; | ||
| 873 | |||
| 874 | if (params.Has("motion")) { | ||
| 875 | const auto motion_sensor = params.Get("motion", 0); | ||
| 876 | input_engine->PreSetController(identifier); | ||
| 877 | input_engine->PreSetMotion(identifier, motion_sensor); | ||
| 878 | return std::make_unique<InputFromMotion>(identifier, motion_sensor, input_engine.get()); | ||
| 879 | } | ||
| 880 | |||
| 881 | const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f); | ||
| 882 | const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); | ||
| 883 | const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); | ||
| 884 | |||
| 885 | const auto axis_x = params.Get("axis_x", 0); | ||
| 886 | const Common::Input::AnalogProperties properties_x = { | ||
| 887 | .deadzone = deadzone, | ||
| 888 | .range = range, | ||
| 889 | .threshold = threshold, | ||
| 890 | .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f), | ||
| 891 | .inverted = params.Get("invert_x", "+") == "-", | ||
| 892 | }; | ||
| 893 | |||
| 894 | const auto axis_y = params.Get("axis_y", 1); | ||
| 895 | const Common::Input::AnalogProperties properties_y = { | ||
| 896 | .deadzone = deadzone, | ||
| 897 | .range = range, | ||
| 898 | .threshold = threshold, | ||
| 899 | .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f), | ||
| 900 | .inverted = params.Get("invert_y", "+") != "+", | ||
| 901 | }; | ||
| 902 | |||
| 903 | const auto axis_z = params.Get("axis_z", 1); | ||
| 904 | const Common::Input::AnalogProperties properties_z = { | ||
| 905 | .deadzone = deadzone, | ||
| 906 | .range = range, | ||
| 907 | .threshold = threshold, | ||
| 908 | .offset = std::clamp(params.Get("offset_z", 0.0f), -1.0f, 1.0f), | ||
| 909 | .inverted = params.Get("invert_z", "+") != "+", | ||
| 910 | }; | ||
| 911 | input_engine->PreSetController(identifier); | ||
| 912 | input_engine->PreSetAxis(identifier, axis_x); | ||
| 913 | input_engine->PreSetAxis(identifier, axis_y); | ||
| 914 | input_engine->PreSetAxis(identifier, axis_z); | ||
| 915 | return std::make_unique<InputFromAxisMotion>(identifier, axis_x, axis_y, axis_z, properties_x, | ||
| 916 | properties_y, properties_z, input_engine.get()); | ||
| 917 | } | ||
| 918 | |||
| 919 | InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_) | ||
| 920 | : input_engine(std::move(input_engine_)) {} | ||
| 921 | |||
| 922 | std::unique_ptr<Common::Input::InputDevice> InputFactory::Create( | ||
| 923 | const Common::ParamPackage& params) { | ||
| 924 | if (params.Has("battery")) { | ||
| 925 | return CreateBatteryDevice(params); | ||
| 926 | } | ||
| 927 | if (params.Has("button") && params.Has("axis")) { | ||
| 928 | return CreateTriggerDevice(params); | ||
| 929 | } | ||
| 930 | if (params.Has("button") && params.Has("axis_x") && params.Has("axis_y")) { | ||
| 931 | return CreateTouchDevice(params); | ||
| 932 | } | ||
| 933 | if (params.Has("button") || params.Has("code")) { | ||
| 934 | return CreateButtonDevice(params); | ||
| 935 | } | ||
| 936 | if (params.Has("hat")) { | ||
| 937 | return CreateHatButtonDevice(params); | ||
| 938 | } | ||
| 939 | if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) { | ||
| 940 | return CreateMotionDevice(params); | ||
| 941 | } | ||
| 942 | if (params.Has("motion")) { | ||
| 943 | return CreateMotionDevice(params); | ||
| 944 | } | ||
| 945 | if (params.Has("axis_x") && params.Has("axis_y")) { | ||
| 946 | return CreateStickDevice(params); | ||
| 947 | } | ||
| 948 | if (params.Has("axis")) { | ||
| 949 | return CreateAnalogDevice(params); | ||
| 950 | } | ||
| 951 | LOG_ERROR(Input, "Invalid parameters given"); | ||
| 952 | return std::make_unique<DummyInput>(); | ||
| 953 | } | ||
| 954 | |||
| 955 | OutputFactory::OutputFactory(std::shared_ptr<InputEngine> input_engine_) | ||
| 956 | : input_engine(std::move(input_engine_)) {} | ||
| 957 | |||
| 958 | std::unique_ptr<Common::Input::OutputDevice> OutputFactory::Create( | ||
| 959 | const Common::ParamPackage& params) { | ||
| 960 | const PadIdentifier identifier = { | ||
| 961 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 962 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 963 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 964 | }; | ||
| 965 | |||
| 966 | input_engine->PreSetController(identifier); | ||
| 967 | return std::make_unique<OutputFromIdentifier>(identifier, input_engine.get()); | ||
| 968 | } | ||
| 969 | |||
| 970 | } // namespace InputCommon | ||
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h new file mode 100644 index 000000000..8a0977d58 --- /dev/null +++ b/src/input_common/input_poller.h | |||
| @@ -0,0 +1,217 @@ | |||
| 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 Input { | ||
| 8 | class InputDevice; | ||
| 9 | |||
| 10 | template <typename InputDevice> | ||
| 11 | class Factory; | ||
| 12 | }; // namespace Input | ||
| 13 | |||
| 14 | namespace InputCommon { | ||
| 15 | class InputEngine; | ||
| 16 | |||
| 17 | class OutputFactory final : public Common::Input::Factory<Common::Input::OutputDevice> { | ||
| 18 | public: | ||
| 19 | explicit OutputFactory(std::shared_ptr<InputEngine> input_engine_); | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Creates an output device from the parameters given. | ||
| 23 | * @param params contains parameters for creating the device: | ||
| 24 | * - "guid" text string for identifying controllers | ||
| 25 | * - "port": port of the connected device | ||
| 26 | * - "pad": slot of the connected controller | ||
| 27 | * @returns a unique output device with the parameters specified | ||
| 28 | */ | ||
| 29 | std::unique_ptr<Common::Input::OutputDevice> Create( | ||
| 30 | const Common::ParamPackage& params) override; | ||
| 31 | |||
| 32 | private: | ||
| 33 | std::shared_ptr<InputEngine> input_engine; | ||
| 34 | }; | ||
| 35 | |||
| 36 | /** | ||
| 37 | * An Input factory. It receives input events and forward them to all input devices it created. | ||
| 38 | */ | ||
| 39 | class InputFactory final : public Common::Input::Factory<Common::Input::InputDevice> { | ||
| 40 | public: | ||
| 41 | explicit InputFactory(std::shared_ptr<InputEngine> input_engine_); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Creates an input device from the parameters given. Identifies the type of input to be | ||
| 45 | * returned if it contains the following parameters: | ||
| 46 | * - button: Contains "button" or "code" | ||
| 47 | * - hat_button: Contains "hat" | ||
| 48 | * - analog: Contains "axis" | ||
| 49 | * - trigger: Contains "button" and "axis" | ||
| 50 | * - stick: Contains "axis_x" and "axis_y" | ||
| 51 | * - motion: Contains "axis_x", "axis_y" and "axis_z" | ||
| 52 | * - motion: Contains "motion" | ||
| 53 | * - touch: Contains "button", "axis_x" and "axis_y" | ||
| 54 | * - battery: Contains "battery" | ||
| 55 | * - output: Contains "output" | ||
| 56 | * @param params contains parameters for creating the device: | ||
| 57 | * - "code": the code of the keyboard key to bind with the input | ||
| 58 | * - "button": same as "code" but for controller buttons | ||
| 59 | * - "hat": similar as "button" but it's a group of hat buttons from SDL | ||
| 60 | * - "axis": the axis number of the axis to bind with the input | ||
| 61 | * - "motion": the motion number of the motion to bind with the input | ||
| 62 | * - "axis_x": same as axis but specifying horizontal direction | ||
| 63 | * - "axis_y": same as axis but specifying vertical direction | ||
| 64 | * - "axis_z": same as axis but specifying forward direction | ||
| 65 | * - "battery": Only used as a placeholder to set the input type | ||
| 66 | * @returns a unique input device with the parameters specified | ||
| 67 | */ | ||
| 68 | std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override; | ||
| 69 | |||
| 70 | private: | ||
| 71 | /** | ||
| 72 | * Creates a button device from the parameters given. | ||
| 73 | * @param params contains parameters for creating the device: | ||
| 74 | * - "code": the code of the keyboard key to bind with the input | ||
| 75 | * - "button": same as "code" but for controller buttons | ||
| 76 | * - "toggle": press once to enable, press again to disable | ||
| 77 | * - "inverted": inverts the output of the button | ||
| 78 | * - "guid": text string for identifying controllers | ||
| 79 | * - "port": port of the connected device | ||
| 80 | * - "pad": slot of the connected controller | ||
| 81 | * @returns a unique input device with the parameters specified | ||
| 82 | */ | ||
| 83 | std::unique_ptr<Common::Input::InputDevice> CreateButtonDevice( | ||
| 84 | const Common::ParamPackage& params); | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Creates a hat button device from the parameters given. | ||
| 88 | * @param params contains parameters for creating the device: | ||
| 89 | * - "button": the controller hat id to bind with the input | ||
| 90 | * - "direction": the direction id to be detected | ||
| 91 | * - "toggle": press once to enable, press again to disable | ||
| 92 | * - "inverted": inverts the output of the button | ||
| 93 | * - "guid": text string for identifying controllers | ||
| 94 | * - "port": port of the connected device | ||
| 95 | * - "pad": slot of the connected controller | ||
| 96 | * @returns a unique input device with the parameters specified | ||
| 97 | */ | ||
| 98 | std::unique_ptr<Common::Input::InputDevice> CreateHatButtonDevice( | ||
| 99 | const Common::ParamPackage& params); | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Creates a stick device from the parameters given. | ||
| 103 | * @param params contains parameters for creating the device: | ||
| 104 | * - "axis_x": the controller horizontal axis id to bind with the input | ||
| 105 | * - "axis_y": the controller vertical axis id to bind with the input | ||
| 106 | * - "deadzone": the minimum required value to be detected | ||
| 107 | * - "range": the maximum value required to reach 100% | ||
| 108 | * - "threshold": the minimum required value to considered pressed | ||
| 109 | * - "offset_x": the amount of offset in the x axis | ||
| 110 | * - "offset_y": the amount of offset in the y axis | ||
| 111 | * - "invert_x": inverts the sign of the horizontal axis | ||
| 112 | * - "invert_y": inverts the sign of the vertical axis | ||
| 113 | * - "guid": text string for identifying controllers | ||
| 114 | * - "port": port of the connected device | ||
| 115 | * - "pad": slot of the connected controller | ||
| 116 | * @returns a unique input device with the parameters specified | ||
| 117 | */ | ||
| 118 | std::unique_ptr<Common::Input::InputDevice> CreateStickDevice( | ||
| 119 | const Common::ParamPackage& params); | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Creates an analog device from the parameters given. | ||
| 123 | * @param params contains parameters for creating the device: | ||
| 124 | * - "axis": the controller axis id to bind with the input | ||
| 125 | * - "deadzone": the minimum required value to be detected | ||
| 126 | * - "range": the maximum value required to reach 100% | ||
| 127 | * - "threshold": the minimum required value to considered pressed | ||
| 128 | * - "offset": the amount of offset in the axis | ||
| 129 | * - "invert": inverts the sign of the axis | ||
| 130 | * - "guid": text string for identifying controllers | ||
| 131 | * - "port": port of the connected device | ||
| 132 | * - "pad": slot of the connected controller | ||
| 133 | * @returns a unique input device with the parameters specified | ||
| 134 | */ | ||
| 135 | std::unique_ptr<Common::Input::InputDevice> CreateAnalogDevice( | ||
| 136 | const Common::ParamPackage& params); | ||
| 137 | |||
| 138 | /** | ||
| 139 | * Creates a trigger device from the parameters given. | ||
| 140 | * @param params contains parameters for creating the device: | ||
| 141 | * - "button": the controller hat id to bind with the input | ||
| 142 | * - "direction": the direction id to be detected | ||
| 143 | * - "toggle": press once to enable, press again to disable | ||
| 144 | * - "inverted": inverts the output of the button | ||
| 145 | * - "axis": the controller axis id to bind with the input | ||
| 146 | * - "deadzone": the minimum required value to be detected | ||
| 147 | * - "range": the maximum value required to reach 100% | ||
| 148 | * - "threshold": the minimum required value to considered pressed | ||
| 149 | * - "offset": the amount of offset in the axis | ||
| 150 | * - "invert": inverts the sign of the axis | ||
| 151 | * - "guid": text string for identifying controllers | ||
| 152 | * - "port": port of the connected device | ||
| 153 | * - "pad": slot of the connected controller | ||
| 154 | * @returns a unique input device with the parameters specified | ||
| 155 | */ | ||
| 156 | std::unique_ptr<Common::Input::InputDevice> CreateTriggerDevice( | ||
| 157 | const Common::ParamPackage& params); | ||
| 158 | |||
| 159 | /** | ||
| 160 | * Creates a touch device from the parameters given. | ||
| 161 | * @param params contains parameters for creating the device: | ||
| 162 | * - "button": the controller hat id to bind with the input | ||
| 163 | * - "direction": the direction id to be detected | ||
| 164 | * - "toggle": press once to enable, press again to disable | ||
| 165 | * - "inverted": inverts the output of the button | ||
| 166 | * - "axis_x": the controller horizontal axis id to bind with the input | ||
| 167 | * - "axis_y": the controller vertical axis id to bind with the input | ||
| 168 | * - "deadzone": the minimum required value to be detected | ||
| 169 | * - "range": the maximum value required to reach 100% | ||
| 170 | * - "threshold": the minimum required value to considered pressed | ||
| 171 | * - "offset_x": the amount of offset in the x axis | ||
| 172 | * - "offset_y": the amount of offset in the y axis | ||
| 173 | * - "invert_x": inverts the sign of the horizontal axis | ||
| 174 | * - "invert_y": inverts the sign of the vertical axis | ||
| 175 | * - "guid": text string for identifying controllers | ||
| 176 | * - "port": port of the connected device | ||
| 177 | * - "pad": slot of the connected controller | ||
| 178 | * @returns a unique input device with the parameters specified | ||
| 179 | */ | ||
| 180 | std::unique_ptr<Common::Input::InputDevice> CreateTouchDevice( | ||
| 181 | const Common::ParamPackage& params); | ||
| 182 | |||
| 183 | /** | ||
| 184 | * Creates a battery device from the parameters given. | ||
| 185 | * @param params contains parameters for creating the device: | ||
| 186 | * - "guid": text string for identifying controllers | ||
| 187 | * - "port": port of the connected device | ||
| 188 | * - "pad": slot of the connected controller | ||
| 189 | * @returns a unique input device with the parameters specified | ||
| 190 | */ | ||
| 191 | std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice( | ||
| 192 | const Common::ParamPackage& params); | ||
| 193 | |||
| 194 | /** | ||
| 195 | * Creates a motion device from the parameters given. | ||
| 196 | * @param params contains parameters for creating the device: | ||
| 197 | * - "axis_x": the controller horizontal axis id to bind with the input | ||
| 198 | * - "axis_y": the controller vertical axis id to bind with the input | ||
| 199 | * - "axis_z": the controller forward axis id to bind with the input | ||
| 200 | * - "deadzone": the minimum required value to be detected | ||
| 201 | * - "range": the maximum value required to reach 100% | ||
| 202 | * - "offset_x": the amount of offset in the x axis | ||
| 203 | * - "offset_y": the amount of offset in the y axis | ||
| 204 | * - "offset_z": the amount of offset in the z axis | ||
| 205 | * - "invert_x": inverts the sign of the horizontal axis | ||
| 206 | * - "invert_y": inverts the sign of the vertical axis | ||
| 207 | * - "invert_z": inverts the sign of the forward axis | ||
| 208 | * - "guid": text string for identifying controllers | ||
| 209 | * - "port": port of the connected device | ||
| 210 | * - "pad": slot of the connected controller | ||
| 211 | * @returns a unique input device with the parameters specified | ||
| 212 | */ | ||
| 213 | std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params); | ||
| 214 | |||
| 215 | std::shared_ptr<InputEngine> input_engine; | ||
| 216 | }; | ||
| 217 | } // namespace InputCommon | ||
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp deleted file mode 100644 index 8261e76fd..000000000 --- a/src/input_common/keyboard.cpp +++ /dev/null | |||
| @@ -1,121 +0,0 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <atomic> | ||
| 6 | #include <list> | ||
| 7 | #include <mutex> | ||
| 8 | #include <utility> | ||
| 9 | #include "input_common/keyboard.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | class KeyButton final : public Input::ButtonDevice { | ||
| 14 | public: | ||
| 15 | explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_, bool toggle_) | ||
| 16 | : key_button_list(std::move(key_button_list_)), toggle(toggle_) {} | ||
| 17 | |||
| 18 | ~KeyButton() override; | ||
| 19 | |||
| 20 | bool GetStatus() const override { | ||
| 21 | if (toggle) { | ||
| 22 | return toggled_status.load(std::memory_order_relaxed); | ||
| 23 | } | ||
| 24 | return status.load(); | ||
| 25 | } | ||
| 26 | |||
| 27 | void ToggleButton() { | ||
| 28 | if (lock) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | lock = true; | ||
| 32 | const bool old_toggle_status = toggled_status.load(); | ||
| 33 | toggled_status.store(!old_toggle_status); | ||
| 34 | } | ||
| 35 | |||
| 36 | void UnlockButton() { | ||
| 37 | lock = false; | ||
| 38 | } | ||
| 39 | |||
| 40 | friend class KeyButtonList; | ||
| 41 | |||
| 42 | private: | ||
| 43 | std::shared_ptr<KeyButtonList> key_button_list; | ||
| 44 | std::atomic<bool> status{false}; | ||
| 45 | std::atomic<bool> toggled_status{false}; | ||
| 46 | bool lock{false}; | ||
| 47 | const bool toggle; | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct KeyButtonPair { | ||
| 51 | int key_code; | ||
| 52 | KeyButton* key_button; | ||
| 53 | }; | ||
| 54 | |||
| 55 | class KeyButtonList { | ||
| 56 | public: | ||
| 57 | void AddKeyButton(int key_code, KeyButton* key_button) { | ||
| 58 | std::lock_guard guard{mutex}; | ||
| 59 | list.push_back(KeyButtonPair{key_code, key_button}); | ||
| 60 | } | ||
| 61 | |||
| 62 | void RemoveKeyButton(const KeyButton* key_button) { | ||
| 63 | std::lock_guard guard{mutex}; | ||
| 64 | list.remove_if( | ||
| 65 | [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; }); | ||
| 66 | } | ||
| 67 | |||
| 68 | void ChangeKeyStatus(int key_code, bool pressed) { | ||
| 69 | std::lock_guard guard{mutex}; | ||
| 70 | for (const KeyButtonPair& pair : list) { | ||
| 71 | if (pair.key_code == key_code) { | ||
| 72 | pair.key_button->status.store(pressed); | ||
| 73 | if (pressed) { | ||
| 74 | pair.key_button->ToggleButton(); | ||
| 75 | } else { | ||
| 76 | pair.key_button->UnlockButton(); | ||
| 77 | } | ||
| 78 | pair.key_button->TriggerOnChange(); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | void ChangeAllKeyStatus(bool pressed) { | ||
| 84 | std::lock_guard guard{mutex}; | ||
| 85 | for (const KeyButtonPair& pair : list) { | ||
| 86 | pair.key_button->status.store(pressed); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | private: | ||
| 91 | std::mutex mutex; | ||
| 92 | std::list<KeyButtonPair> list; | ||
| 93 | }; | ||
| 94 | |||
| 95 | Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {} | ||
| 96 | |||
| 97 | KeyButton::~KeyButton() { | ||
| 98 | key_button_list->RemoveKeyButton(this); | ||
| 99 | } | ||
| 100 | |||
| 101 | std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) { | ||
| 102 | const int key_code = params.Get("code", 0); | ||
| 103 | const bool toggle = params.Get("toggle", false); | ||
| 104 | std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list, toggle); | ||
| 105 | key_button_list->AddKeyButton(key_code, button.get()); | ||
| 106 | return button; | ||
| 107 | } | ||
| 108 | |||
| 109 | void Keyboard::PressKey(int key_code) { | ||
| 110 | key_button_list->ChangeKeyStatus(key_code, true); | ||
| 111 | } | ||
| 112 | |||
| 113 | void Keyboard::ReleaseKey(int key_code) { | ||
| 114 | key_button_list->ChangeKeyStatus(key_code, false); | ||
| 115 | } | ||
| 116 | |||
| 117 | void Keyboard::ReleaseAllKeys() { | ||
| 118 | key_button_list->ChangeAllKeyStatus(false); | ||
| 119 | } | ||
| 120 | |||
| 121 | } // namespace InputCommon | ||
diff --git a/src/input_common/keyboard.h b/src/input_common/keyboard.h deleted file mode 100644 index 861950472..000000000 --- a/src/input_common/keyboard.h +++ /dev/null | |||
| @@ -1,47 +0,0 @@ | |||
| 1 | // Copyright 2017 Citra 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 | #include "core/frontend/input.h" | ||
| 9 | |||
| 10 | namespace InputCommon { | ||
| 11 | |||
| 12 | class KeyButtonList; | ||
| 13 | |||
| 14 | /** | ||
| 15 | * A button device factory representing a keyboard. It receives keyboard events and forward them | ||
| 16 | * to all button devices it created. | ||
| 17 | */ | ||
| 18 | class Keyboard final : public Input::Factory<Input::ButtonDevice> { | ||
| 19 | public: | ||
| 20 | Keyboard(); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Creates a button device from a keyboard key | ||
| 24 | * @param params contains parameters for creating the device: | ||
| 25 | * - "code": the code of the key to bind with the button | ||
| 26 | */ | ||
| 27 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Sets the status of all buttons bound with the key to pressed | ||
| 31 | * @param key_code the code of the key to press | ||
| 32 | */ | ||
| 33 | void PressKey(int key_code); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Sets the status of all buttons bound with the key to released | ||
| 37 | * @param key_code the code of the key to release | ||
| 38 | */ | ||
| 39 | void ReleaseKey(int key_code); | ||
| 40 | |||
| 41 | void ReleaseAllKeys(); | ||
| 42 | |||
| 43 | private: | ||
| 44 | std::shared_ptr<KeyButtonList> key_button_list; | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // namespace InputCommon | ||
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index f3907c65a..940744c5f 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -4,146 +4,173 @@ | |||
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <thread> | 6 | #include <thread> |
| 7 | #include "common/input.h" | ||
| 7 | #include "common/param_package.h" | 8 | #include "common/param_package.h" |
| 8 | #include "common/settings.h" | 9 | #include "input_common/drivers/gc_adapter.h" |
| 9 | #include "input_common/analog_from_button.h" | 10 | #include "input_common/drivers/keyboard.h" |
| 10 | #include "input_common/gcadapter/gc_adapter.h" | 11 | #include "input_common/drivers/mouse.h" |
| 11 | #include "input_common/gcadapter/gc_poller.h" | 12 | #include "input_common/drivers/tas_input.h" |
| 12 | #include "input_common/keyboard.h" | 13 | #include "input_common/drivers/touch_screen.h" |
| 14 | #include "input_common/drivers/udp_client.h" | ||
| 15 | #include "input_common/helpers/stick_from_buttons.h" | ||
| 16 | #include "input_common/helpers/touch_from_buttons.h" | ||
| 17 | #include "input_common/input_engine.h" | ||
| 18 | #include "input_common/input_mapping.h" | ||
| 19 | #include "input_common/input_poller.h" | ||
| 13 | #include "input_common/main.h" | 20 | #include "input_common/main.h" |
| 14 | #include "input_common/motion_from_button.h" | ||
| 15 | #include "input_common/mouse/mouse_input.h" | ||
| 16 | #include "input_common/mouse/mouse_poller.h" | ||
| 17 | #include "input_common/tas/tas_input.h" | ||
| 18 | #include "input_common/tas/tas_poller.h" | ||
| 19 | #include "input_common/touch_from_button.h" | ||
| 20 | #include "input_common/udp/client.h" | ||
| 21 | #include "input_common/udp/udp.h" | ||
| 22 | #ifdef HAVE_SDL2 | 21 | #ifdef HAVE_SDL2 |
| 23 | #include "input_common/sdl/sdl.h" | 22 | #include "input_common/drivers/sdl_driver.h" |
| 24 | #endif | 23 | #endif |
| 25 | 24 | ||
| 26 | namespace InputCommon { | 25 | namespace InputCommon { |
| 27 | 26 | ||
| 28 | struct InputSubsystem::Impl { | 27 | struct InputSubsystem::Impl { |
| 29 | void Initialize() { | 28 | void Initialize() { |
| 30 | gcadapter = std::make_shared<GCAdapter::Adapter>(); | 29 | mapping_factory = std::make_shared<MappingFactory>(); |
| 31 | gcbuttons = std::make_shared<GCButtonFactory>(gcadapter); | 30 | MappingCallback mapping_callback{[this](MappingData data) { RegisterInput(data); }}; |
| 32 | Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); | 31 | |
| 33 | gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); | 32 | keyboard = std::make_shared<Keyboard>("keyboard"); |
| 34 | Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); | 33 | keyboard->SetMappingCallback(mapping_callback); |
| 35 | gcvibration = std::make_shared<GCVibrationFactory>(gcadapter); | 34 | keyboard_factory = std::make_shared<InputFactory>(keyboard); |
| 36 | Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration); | 35 | keyboard_output_factory = std::make_shared<OutputFactory>(keyboard); |
| 37 | 36 | Common::Input::RegisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName(), | |
| 38 | keyboard = std::make_shared<Keyboard>(); | 37 | keyboard_factory); |
| 39 | Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); | 38 | Common::Input::RegisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName(), |
| 40 | Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", | 39 | keyboard_output_factory); |
| 41 | std::make_shared<AnalogFromButton>()); | 40 | |
| 42 | Input::RegisterFactory<Input::MotionDevice>("keyboard", | 41 | mouse = std::make_shared<Mouse>("mouse"); |
| 43 | std::make_shared<MotionFromButton>()); | 42 | mouse->SetMappingCallback(mapping_callback); |
| 44 | Input::RegisterFactory<Input::TouchDevice>("touch_from_button", | 43 | mouse_factory = std::make_shared<InputFactory>(mouse); |
| 45 | std::make_shared<TouchFromButtonFactory>()); | 44 | mouse_output_factory = std::make_shared<OutputFactory>(mouse); |
| 45 | Common::Input::RegisterFactory<Common::Input::InputDevice>(mouse->GetEngineName(), | ||
| 46 | mouse_factory); | ||
| 47 | Common::Input::RegisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName(), | ||
| 48 | mouse_output_factory); | ||
| 49 | |||
| 50 | touch_screen = std::make_shared<TouchScreen>("touch"); | ||
| 51 | touch_screen_factory = std::make_shared<InputFactory>(touch_screen); | ||
| 52 | Common::Input::RegisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName(), | ||
| 53 | touch_screen_factory); | ||
| 54 | |||
| 55 | gcadapter = std::make_shared<GCAdapter>("gcpad"); | ||
| 56 | gcadapter->SetMappingCallback(mapping_callback); | ||
| 57 | gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter); | ||
| 58 | gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter); | ||
| 59 | Common::Input::RegisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName(), | ||
| 60 | gcadapter_input_factory); | ||
| 61 | Common::Input::RegisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName(), | ||
| 62 | gcadapter_output_factory); | ||
| 63 | |||
| 64 | udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp"); | ||
| 65 | udp_client->SetMappingCallback(mapping_callback); | ||
| 66 | udp_client_input_factory = std::make_shared<InputFactory>(udp_client); | ||
| 67 | udp_client_output_factory = std::make_shared<OutputFactory>(udp_client); | ||
| 68 | Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(), | ||
| 69 | udp_client_input_factory); | ||
| 70 | Common::Input::RegisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName(), | ||
| 71 | udp_client_output_factory); | ||
| 72 | |||
| 73 | tas_input = std::make_shared<TasInput::Tas>("tas"); | ||
| 74 | tas_input->SetMappingCallback(mapping_callback); | ||
| 75 | tas_input_factory = std::make_shared<InputFactory>(tas_input); | ||
| 76 | tas_output_factory = std::make_shared<OutputFactory>(tas_input); | ||
| 77 | Common::Input::RegisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName(), | ||
| 78 | tas_input_factory); | ||
| 79 | Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(), | ||
| 80 | tas_output_factory); | ||
| 46 | 81 | ||
| 47 | #ifdef HAVE_SDL2 | 82 | #ifdef HAVE_SDL2 |
| 48 | sdl = SDL::Init(); | 83 | sdl = std::make_shared<SDLDriver>("sdl"); |
| 84 | sdl->SetMappingCallback(mapping_callback); | ||
| 85 | sdl_input_factory = std::make_shared<InputFactory>(sdl); | ||
| 86 | sdl_output_factory = std::make_shared<OutputFactory>(sdl); | ||
| 87 | Common::Input::RegisterFactory<Common::Input::InputDevice>(sdl->GetEngineName(), | ||
| 88 | sdl_input_factory); | ||
| 89 | Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(), | ||
| 90 | sdl_output_factory); | ||
| 49 | #endif | 91 | #endif |
| 50 | 92 | ||
| 51 | udp = std::make_shared<InputCommon::CemuhookUDP::Client>(); | 93 | Common::Input::RegisterFactory<Common::Input::InputDevice>( |
| 52 | udpmotion = std::make_shared<UDPMotionFactory>(udp); | 94 | "touch_from_button", std::make_shared<TouchFromButton>()); |
| 53 | Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion); | 95 | Common::Input::RegisterFactory<Common::Input::InputDevice>( |
| 54 | udptouch = std::make_shared<UDPTouchFactory>(udp); | 96 | "analog_from_button", std::make_shared<StickFromButton>()); |
| 55 | Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch); | ||
| 56 | |||
| 57 | mouse = std::make_shared<MouseInput::Mouse>(); | ||
| 58 | mousebuttons = std::make_shared<MouseButtonFactory>(mouse); | ||
| 59 | Input::RegisterFactory<Input::ButtonDevice>("mouse", mousebuttons); | ||
| 60 | mouseanalog = std::make_shared<MouseAnalogFactory>(mouse); | ||
| 61 | Input::RegisterFactory<Input::AnalogDevice>("mouse", mouseanalog); | ||
| 62 | mousemotion = std::make_shared<MouseMotionFactory>(mouse); | ||
| 63 | Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion); | ||
| 64 | mousetouch = std::make_shared<MouseTouchFactory>(mouse); | ||
| 65 | Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch); | ||
| 66 | |||
| 67 | tas = std::make_shared<TasInput::Tas>(); | ||
| 68 | tasbuttons = std::make_shared<TasButtonFactory>(tas); | ||
| 69 | Input::RegisterFactory<Input::ButtonDevice>("tas", tasbuttons); | ||
| 70 | tasanalog = std::make_shared<TasAnalogFactory>(tas); | ||
| 71 | Input::RegisterFactory<Input::AnalogDevice>("tas", tasanalog); | ||
| 72 | } | 97 | } |
| 73 | 98 | ||
| 74 | void Shutdown() { | 99 | void Shutdown() { |
| 75 | Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); | 100 | Common::Input::UnregisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName()); |
| 76 | Input::UnregisterFactory<Input::MotionDevice>("keyboard"); | 101 | Common::Input::UnregisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName()); |
| 77 | keyboard.reset(); | 102 | keyboard.reset(); |
| 78 | Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); | ||
| 79 | Input::UnregisterFactory<Input::TouchDevice>("touch_from_button"); | ||
| 80 | #ifdef HAVE_SDL2 | ||
| 81 | sdl.reset(); | ||
| 82 | #endif | ||
| 83 | Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); | ||
| 84 | Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); | ||
| 85 | Input::UnregisterFactory<Input::VibrationDevice>("gcpad"); | ||
| 86 | 103 | ||
| 87 | gcbuttons.reset(); | 104 | Common::Input::UnregisterFactory<Common::Input::InputDevice>(mouse->GetEngineName()); |
| 88 | gcanalog.reset(); | 105 | Common::Input::UnregisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName()); |
| 89 | gcvibration.reset(); | 106 | mouse.reset(); |
| 90 | 107 | ||
| 91 | Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); | 108 | Common::Input::UnregisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName()); |
| 92 | Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); | 109 | touch_screen.reset(); |
| 93 | 110 | ||
| 94 | udpmotion.reset(); | 111 | Common::Input::UnregisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName()); |
| 95 | udptouch.reset(); | 112 | Common::Input::UnregisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName()); |
| 113 | gcadapter.reset(); | ||
| 96 | 114 | ||
| 97 | Input::UnregisterFactory<Input::ButtonDevice>("mouse"); | 115 | Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName()); |
| 98 | Input::UnregisterFactory<Input::AnalogDevice>("mouse"); | 116 | Common::Input::UnregisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName()); |
| 99 | Input::UnregisterFactory<Input::MotionDevice>("mouse"); | 117 | udp_client.reset(); |
| 100 | Input::UnregisterFactory<Input::TouchDevice>("mouse"); | ||
| 101 | 118 | ||
| 102 | mousebuttons.reset(); | 119 | Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName()); |
| 103 | mouseanalog.reset(); | 120 | Common::Input::UnregisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName()); |
| 104 | mousemotion.reset(); | 121 | tas_input.reset(); |
| 105 | mousetouch.reset(); | ||
| 106 | 122 | ||
| 107 | Input::UnregisterFactory<Input::ButtonDevice>("tas"); | 123 | #ifdef HAVE_SDL2 |
| 108 | Input::UnregisterFactory<Input::AnalogDevice>("tas"); | 124 | Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName()); |
| 125 | Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName()); | ||
| 126 | sdl.reset(); | ||
| 127 | #endif | ||
| 109 | 128 | ||
| 110 | tasbuttons.reset(); | 129 | Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button"); |
| 111 | tasanalog.reset(); | 130 | Common::Input::UnregisterFactory<Common::Input::InputDevice>("analog_from_button"); |
| 112 | } | 131 | } |
| 113 | 132 | ||
| 114 | [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { | 133 | [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { |
| 115 | std::vector<Common::ParamPackage> devices = { | 134 | std::vector<Common::ParamPackage> devices = { |
| 116 | Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, | 135 | Common::ParamPackage{{"display", "Any"}, {"engine", "any"}}, |
| 117 | Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}}, | ||
| 118 | }; | 136 | }; |
| 119 | if (Settings::values.tas_enable) { | 137 | |
| 120 | devices.emplace_back( | 138 | auto keyboard_devices = keyboard->GetInputDevices(); |
| 121 | Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}}); | 139 | devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end()); |
| 122 | } | 140 | auto mouse_devices = mouse->GetInputDevices(); |
| 141 | devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end()); | ||
| 142 | auto gcadapter_devices = gcadapter->GetInputDevices(); | ||
| 143 | devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end()); | ||
| 144 | auto udp_devices = udp_client->GetInputDevices(); | ||
| 145 | devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); | ||
| 123 | #ifdef HAVE_SDL2 | 146 | #ifdef HAVE_SDL2 |
| 124 | auto sdl_devices = sdl->GetInputDevices(); | 147 | auto sdl_devices = sdl->GetInputDevices(); |
| 125 | devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); | 148 | devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); |
| 126 | #endif | 149 | #endif |
| 127 | auto udp_devices = udp->GetInputDevices(); | 150 | |
| 128 | devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); | ||
| 129 | auto gcpad_devices = gcadapter->GetInputDevices(); | ||
| 130 | devices.insert(devices.end(), gcpad_devices.begin(), gcpad_devices.end()); | ||
| 131 | return devices; | 151 | return devices; |
| 132 | } | 152 | } |
| 133 | 153 | ||
| 134 | [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( | 154 | [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( |
| 135 | const Common::ParamPackage& params) const { | 155 | const Common::ParamPackage& params) const { |
| 136 | if (!params.Has("class") || params.Get("class", "") == "any") { | 156 | if (!params.Has("engine") || params.Get("engine", "") == "any") { |
| 137 | return {}; | 157 | return {}; |
| 138 | } | 158 | } |
| 139 | if (params.Get("class", "") == "gcpad") { | 159 | const std::string engine = params.Get("engine", ""); |
| 160 | if (engine == mouse->GetEngineName()) { | ||
| 161 | return mouse->GetAnalogMappingForDevice(params); | ||
| 162 | } | ||
| 163 | if (engine == gcadapter->GetEngineName()) { | ||
| 140 | return gcadapter->GetAnalogMappingForDevice(params); | 164 | return gcadapter->GetAnalogMappingForDevice(params); |
| 141 | } | 165 | } |
| 142 | if (params.Get("class", "") == "tas") { | 166 | if (engine == udp_client->GetEngineName()) { |
| 143 | return tas->GetAnalogMappingForDevice(params); | 167 | return udp_client->GetAnalogMappingForDevice(params); |
| 168 | } | ||
| 169 | if (engine == tas_input->GetEngineName()) { | ||
| 170 | return tas_input->GetAnalogMappingForDevice(params); | ||
| 144 | } | 171 | } |
| 145 | #ifdef HAVE_SDL2 | 172 | #ifdef HAVE_SDL2 |
| 146 | if (params.Get("class", "") == "sdl") { | 173 | if (engine == sdl->GetEngineName()) { |
| 147 | return sdl->GetAnalogMappingForDevice(params); | 174 | return sdl->GetAnalogMappingForDevice(params); |
| 148 | } | 175 | } |
| 149 | #endif | 176 | #endif |
| @@ -152,17 +179,21 @@ struct InputSubsystem::Impl { | |||
| 152 | 179 | ||
| 153 | [[nodiscard]] ButtonMapping GetButtonMappingForDevice( | 180 | [[nodiscard]] ButtonMapping GetButtonMappingForDevice( |
| 154 | const Common::ParamPackage& params) const { | 181 | const Common::ParamPackage& params) const { |
| 155 | if (!params.Has("class") || params.Get("class", "") == "any") { | 182 | if (!params.Has("engine") || params.Get("engine", "") == "any") { |
| 156 | return {}; | 183 | return {}; |
| 157 | } | 184 | } |
| 158 | if (params.Get("class", "") == "gcpad") { | 185 | const std::string engine = params.Get("engine", ""); |
| 186 | if (engine == gcadapter->GetEngineName()) { | ||
| 159 | return gcadapter->GetButtonMappingForDevice(params); | 187 | return gcadapter->GetButtonMappingForDevice(params); |
| 160 | } | 188 | } |
| 161 | if (params.Get("class", "") == "tas") { | 189 | if (engine == udp_client->GetEngineName()) { |
| 162 | return tas->GetButtonMappingForDevice(params); | 190 | return udp_client->GetButtonMappingForDevice(params); |
| 191 | } | ||
| 192 | if (engine == tas_input->GetEngineName()) { | ||
| 193 | return tas_input->GetButtonMappingForDevice(params); | ||
| 163 | } | 194 | } |
| 164 | #ifdef HAVE_SDL2 | 195 | #ifdef HAVE_SDL2 |
| 165 | if (params.Get("class", "") == "sdl") { | 196 | if (engine == sdl->GetEngineName()) { |
| 166 | return sdl->GetButtonMappingForDevice(params); | 197 | return sdl->GetButtonMappingForDevice(params); |
| 167 | } | 198 | } |
| 168 | #endif | 199 | #endif |
| @@ -171,40 +202,119 @@ struct InputSubsystem::Impl { | |||
| 171 | 202 | ||
| 172 | [[nodiscard]] MotionMapping GetMotionMappingForDevice( | 203 | [[nodiscard]] MotionMapping GetMotionMappingForDevice( |
| 173 | const Common::ParamPackage& params) const { | 204 | const Common::ParamPackage& params) const { |
| 174 | if (!params.Has("class") || params.Get("class", "") == "any") { | 205 | if (!params.Has("engine") || params.Get("engine", "") == "any") { |
| 175 | return {}; | 206 | return {}; |
| 176 | } | 207 | } |
| 177 | if (params.Get("class", "") == "cemuhookudp") { | 208 | const std::string engine = params.Get("engine", ""); |
| 178 | // TODO return the correct motion device | 209 | if (engine == udp_client->GetEngineName()) { |
| 179 | return {}; | 210 | return udp_client->GetMotionMappingForDevice(params); |
| 180 | } | 211 | } |
| 181 | #ifdef HAVE_SDL2 | 212 | #ifdef HAVE_SDL2 |
| 182 | if (params.Get("class", "") == "sdl") { | 213 | if (engine == sdl->GetEngineName()) { |
| 183 | return sdl->GetMotionMappingForDevice(params); | 214 | return sdl->GetMotionMappingForDevice(params); |
| 184 | } | 215 | } |
| 185 | #endif | 216 | #endif |
| 186 | return {}; | 217 | return {}; |
| 187 | } | 218 | } |
| 188 | 219 | ||
| 220 | Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const { | ||
| 221 | if (!params.Has("engine") || params.Get("engine", "") == "any") { | ||
| 222 | return Common::Input::ButtonNames::Undefined; | ||
| 223 | } | ||
| 224 | const std::string engine = params.Get("engine", ""); | ||
| 225 | if (engine == mouse->GetEngineName()) { | ||
| 226 | return mouse->GetUIName(params); | ||
| 227 | } | ||
| 228 | if (engine == gcadapter->GetEngineName()) { | ||
| 229 | return gcadapter->GetUIName(params); | ||
| 230 | } | ||
| 231 | if (engine == udp_client->GetEngineName()) { | ||
| 232 | return udp_client->GetUIName(params); | ||
| 233 | } | ||
| 234 | if (engine == tas_input->GetEngineName()) { | ||
| 235 | return tas_input->GetUIName(params); | ||
| 236 | } | ||
| 237 | #ifdef HAVE_SDL2 | ||
| 238 | if (engine == sdl->GetEngineName()) { | ||
| 239 | return sdl->GetUIName(params); | ||
| 240 | } | ||
| 241 | #endif | ||
| 242 | return Common::Input::ButtonNames::Invalid; | ||
| 243 | } | ||
| 244 | |||
| 245 | bool IsController(const Common::ParamPackage& params) { | ||
| 246 | const std::string engine = params.Get("engine", ""); | ||
| 247 | if (engine == mouse->GetEngineName()) { | ||
| 248 | return true; | ||
| 249 | } | ||
| 250 | if (engine == gcadapter->GetEngineName()) { | ||
| 251 | return true; | ||
| 252 | } | ||
| 253 | if (engine == udp_client->GetEngineName()) { | ||
| 254 | return true; | ||
| 255 | } | ||
| 256 | if (engine == tas_input->GetEngineName()) { | ||
| 257 | return true; | ||
| 258 | } | ||
| 259 | #ifdef HAVE_SDL2 | ||
| 260 | if (engine == sdl->GetEngineName()) { | ||
| 261 | return true; | ||
| 262 | } | ||
| 263 | #endif | ||
| 264 | return false; | ||
| 265 | } | ||
| 266 | |||
| 267 | void BeginConfiguration() { | ||
| 268 | keyboard->BeginConfiguration(); | ||
| 269 | mouse->BeginConfiguration(); | ||
| 270 | gcadapter->BeginConfiguration(); | ||
| 271 | udp_client->BeginConfiguration(); | ||
| 272 | #ifdef HAVE_SDL2 | ||
| 273 | sdl->BeginConfiguration(); | ||
| 274 | #endif | ||
| 275 | } | ||
| 276 | |||
| 277 | void EndConfiguration() { | ||
| 278 | keyboard->EndConfiguration(); | ||
| 279 | mouse->EndConfiguration(); | ||
| 280 | gcadapter->EndConfiguration(); | ||
| 281 | udp_client->EndConfiguration(); | ||
| 282 | #ifdef HAVE_SDL2 | ||
| 283 | sdl->EndConfiguration(); | ||
| 284 | #endif | ||
| 285 | } | ||
| 286 | |||
| 287 | void RegisterInput(MappingData data) { | ||
| 288 | mapping_factory->RegisterInput(data); | ||
| 289 | } | ||
| 290 | |||
| 291 | std::shared_ptr<MappingFactory> mapping_factory; | ||
| 292 | |||
| 189 | std::shared_ptr<Keyboard> keyboard; | 293 | std::shared_ptr<Keyboard> keyboard; |
| 294 | std::shared_ptr<Mouse> mouse; | ||
| 295 | std::shared_ptr<GCAdapter> gcadapter; | ||
| 296 | std::shared_ptr<TouchScreen> touch_screen; | ||
| 297 | std::shared_ptr<TasInput::Tas> tas_input; | ||
| 298 | std::shared_ptr<CemuhookUDP::UDPClient> udp_client; | ||
| 299 | |||
| 300 | std::shared_ptr<InputFactory> keyboard_factory; | ||
| 301 | std::shared_ptr<InputFactory> mouse_factory; | ||
| 302 | std::shared_ptr<InputFactory> gcadapter_input_factory; | ||
| 303 | std::shared_ptr<InputFactory> touch_screen_factory; | ||
| 304 | std::shared_ptr<InputFactory> udp_client_input_factory; | ||
| 305 | std::shared_ptr<InputFactory> tas_input_factory; | ||
| 306 | |||
| 307 | std::shared_ptr<OutputFactory> keyboard_output_factory; | ||
| 308 | std::shared_ptr<OutputFactory> mouse_output_factory; | ||
| 309 | std::shared_ptr<OutputFactory> gcadapter_output_factory; | ||
| 310 | std::shared_ptr<OutputFactory> udp_client_output_factory; | ||
| 311 | std::shared_ptr<OutputFactory> tas_output_factory; | ||
| 312 | |||
| 190 | #ifdef HAVE_SDL2 | 313 | #ifdef HAVE_SDL2 |
| 191 | std::unique_ptr<SDL::State> sdl; | 314 | std::shared_ptr<SDLDriver> sdl; |
| 315 | std::shared_ptr<InputFactory> sdl_input_factory; | ||
| 316 | std::shared_ptr<OutputFactory> sdl_output_factory; | ||
| 192 | #endif | 317 | #endif |
| 193 | std::shared_ptr<GCButtonFactory> gcbuttons; | ||
| 194 | std::shared_ptr<GCAnalogFactory> gcanalog; | ||
| 195 | std::shared_ptr<GCVibrationFactory> gcvibration; | ||
| 196 | std::shared_ptr<UDPMotionFactory> udpmotion; | ||
| 197 | std::shared_ptr<UDPTouchFactory> udptouch; | ||
| 198 | std::shared_ptr<MouseButtonFactory> mousebuttons; | ||
| 199 | std::shared_ptr<MouseAnalogFactory> mouseanalog; | ||
| 200 | std::shared_ptr<MouseMotionFactory> mousemotion; | ||
| 201 | std::shared_ptr<MouseTouchFactory> mousetouch; | ||
| 202 | std::shared_ptr<TasButtonFactory> tasbuttons; | ||
| 203 | std::shared_ptr<TasAnalogFactory> tasanalog; | ||
| 204 | std::shared_ptr<CemuhookUDP::Client> udp; | ||
| 205 | std::shared_ptr<GCAdapter::Adapter> gcadapter; | ||
| 206 | std::shared_ptr<MouseInput::Mouse> mouse; | ||
| 207 | std::shared_ptr<TasInput::Tas> tas; | ||
| 208 | }; | 318 | }; |
| 209 | 319 | ||
| 210 | InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} | 320 | InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} |
| @@ -227,20 +337,28 @@ const Keyboard* InputSubsystem::GetKeyboard() const { | |||
| 227 | return impl->keyboard.get(); | 337 | return impl->keyboard.get(); |
| 228 | } | 338 | } |
| 229 | 339 | ||
| 230 | MouseInput::Mouse* InputSubsystem::GetMouse() { | 340 | Mouse* InputSubsystem::GetMouse() { |
| 231 | return impl->mouse.get(); | 341 | return impl->mouse.get(); |
| 232 | } | 342 | } |
| 233 | 343 | ||
| 234 | const MouseInput::Mouse* InputSubsystem::GetMouse() const { | 344 | const Mouse* InputSubsystem::GetMouse() const { |
| 235 | return impl->mouse.get(); | 345 | return impl->mouse.get(); |
| 236 | } | 346 | } |
| 237 | 347 | ||
| 348 | TouchScreen* InputSubsystem::GetTouchScreen() { | ||
| 349 | return impl->touch_screen.get(); | ||
| 350 | } | ||
| 351 | |||
| 352 | const TouchScreen* InputSubsystem::GetTouchScreen() const { | ||
| 353 | return impl->touch_screen.get(); | ||
| 354 | } | ||
| 355 | |||
| 238 | TasInput::Tas* InputSubsystem::GetTas() { | 356 | TasInput::Tas* InputSubsystem::GetTas() { |
| 239 | return impl->tas.get(); | 357 | return impl->tas_input.get(); |
| 240 | } | 358 | } |
| 241 | 359 | ||
| 242 | const TasInput::Tas* InputSubsystem::GetTas() const { | 360 | const TasInput::Tas* InputSubsystem::GetTas() const { |
| 243 | return impl->tas.get(); | 361 | return impl->tas_input.get(); |
| 244 | } | 362 | } |
| 245 | 363 | ||
| 246 | std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { | 364 | std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { |
| @@ -259,100 +377,30 @@ MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPacka | |||
| 259 | return impl->GetMotionMappingForDevice(device); | 377 | return impl->GetMotionMappingForDevice(device); |
| 260 | } | 378 | } |
| 261 | 379 | ||
| 262 | GCAnalogFactory* InputSubsystem::GetGCAnalogs() { | 380 | Common::Input::ButtonNames InputSubsystem::GetButtonName(const Common::ParamPackage& params) const { |
| 263 | return impl->gcanalog.get(); | 381 | return impl->GetButtonName(params); |
| 264 | } | ||
| 265 | |||
| 266 | const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const { | ||
| 267 | return impl->gcanalog.get(); | ||
| 268 | } | ||
| 269 | |||
| 270 | GCButtonFactory* InputSubsystem::GetGCButtons() { | ||
| 271 | return impl->gcbuttons.get(); | ||
| 272 | } | ||
| 273 | |||
| 274 | const GCButtonFactory* InputSubsystem::GetGCButtons() const { | ||
| 275 | return impl->gcbuttons.get(); | ||
| 276 | } | ||
| 277 | |||
| 278 | UDPMotionFactory* InputSubsystem::GetUDPMotions() { | ||
| 279 | return impl->udpmotion.get(); | ||
| 280 | } | ||
| 281 | |||
| 282 | const UDPMotionFactory* InputSubsystem::GetUDPMotions() const { | ||
| 283 | return impl->udpmotion.get(); | ||
| 284 | } | ||
| 285 | |||
| 286 | UDPTouchFactory* InputSubsystem::GetUDPTouch() { | ||
| 287 | return impl->udptouch.get(); | ||
| 288 | } | ||
| 289 | |||
| 290 | const UDPTouchFactory* InputSubsystem::GetUDPTouch() const { | ||
| 291 | return impl->udptouch.get(); | ||
| 292 | } | ||
| 293 | |||
| 294 | MouseButtonFactory* InputSubsystem::GetMouseButtons() { | ||
| 295 | return impl->mousebuttons.get(); | ||
| 296 | } | 382 | } |
| 297 | 383 | ||
| 298 | const MouseButtonFactory* InputSubsystem::GetMouseButtons() const { | 384 | bool InputSubsystem::IsController(const Common::ParamPackage& params) const { |
| 299 | return impl->mousebuttons.get(); | 385 | return impl->IsController(params); |
| 300 | } | 386 | } |
| 301 | 387 | ||
| 302 | MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() { | 388 | void InputSubsystem::ReloadInputDevices() { |
| 303 | return impl->mouseanalog.get(); | 389 | impl->udp_client.get()->ReloadSockets(); |
| 304 | } | ||
| 305 | |||
| 306 | const MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() const { | ||
| 307 | return impl->mouseanalog.get(); | ||
| 308 | } | ||
| 309 | |||
| 310 | MouseMotionFactory* InputSubsystem::GetMouseMotions() { | ||
| 311 | return impl->mousemotion.get(); | ||
| 312 | } | ||
| 313 | |||
| 314 | const MouseMotionFactory* InputSubsystem::GetMouseMotions() const { | ||
| 315 | return impl->mousemotion.get(); | ||
| 316 | } | ||
| 317 | |||
| 318 | MouseTouchFactory* InputSubsystem::GetMouseTouch() { | ||
| 319 | return impl->mousetouch.get(); | ||
| 320 | } | ||
| 321 | |||
| 322 | const MouseTouchFactory* InputSubsystem::GetMouseTouch() const { | ||
| 323 | return impl->mousetouch.get(); | ||
| 324 | } | ||
| 325 | |||
| 326 | TasButtonFactory* InputSubsystem::GetTasButtons() { | ||
| 327 | return impl->tasbuttons.get(); | ||
| 328 | } | ||
| 329 | |||
| 330 | const TasButtonFactory* InputSubsystem::GetTasButtons() const { | ||
| 331 | return impl->tasbuttons.get(); | ||
| 332 | } | ||
| 333 | |||
| 334 | TasAnalogFactory* InputSubsystem::GetTasAnalogs() { | ||
| 335 | return impl->tasanalog.get(); | ||
| 336 | } | 390 | } |
| 337 | 391 | ||
| 338 | const TasAnalogFactory* InputSubsystem::GetTasAnalogs() const { | 392 | void InputSubsystem::BeginMapping(Polling::InputType type) { |
| 339 | return impl->tasanalog.get(); | 393 | impl->BeginConfiguration(); |
| 394 | impl->mapping_factory->BeginMapping(type); | ||
| 340 | } | 395 | } |
| 341 | 396 | ||
| 342 | void InputSubsystem::ReloadInputDevices() { | 397 | const Common::ParamPackage InputSubsystem::GetNextInput() const { |
| 343 | if (!impl->udp) { | 398 | return impl->mapping_factory->GetNextInput(); |
| 344 | return; | ||
| 345 | } | ||
| 346 | impl->udp->ReloadSockets(); | ||
| 347 | } | 399 | } |
| 348 | 400 | ||
| 349 | std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( | 401 | void InputSubsystem::StopMapping() const { |
| 350 | [[maybe_unused]] Polling::DeviceType type) const { | 402 | impl->EndConfiguration(); |
| 351 | #ifdef HAVE_SDL2 | 403 | impl->mapping_factory->StopMapping(); |
| 352 | return impl->sdl->GetPollers(type); | ||
| 353 | #else | ||
| 354 | return {}; | ||
| 355 | #endif | ||
| 356 | } | 404 | } |
| 357 | 405 | ||
| 358 | std::string GenerateKeyboardParam(int key_code) { | 406 | std::string GenerateKeyboardParam(int key_code) { |
diff --git a/src/input_common/main.h b/src/input_common/main.h index 6390d3f09..c6f97f691 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h | |||
| @@ -13,6 +13,10 @@ namespace Common { | |||
| 13 | class ParamPackage; | 13 | class ParamPackage; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | namespace Common::Input { | ||
| 17 | enum class ButtonNames; | ||
| 18 | } | ||
| 19 | |||
| 16 | namespace Settings::NativeAnalog { | 20 | namespace Settings::NativeAnalog { |
| 17 | enum Values : int; | 21 | enum Values : int; |
| 18 | } | 22 | } |
| @@ -25,56 +29,26 @@ namespace Settings::NativeMotion { | |||
| 25 | enum Values : int; | 29 | enum Values : int; |
| 26 | } | 30 | } |
| 27 | 31 | ||
| 28 | namespace MouseInput { | 32 | namespace InputCommon { |
| 33 | class Keyboard; | ||
| 29 | class Mouse; | 34 | class Mouse; |
| 30 | } | 35 | class TouchScreen; |
| 36 | struct MappingData; | ||
| 37 | } // namespace InputCommon | ||
| 31 | 38 | ||
| 32 | namespace TasInput { | 39 | namespace InputCommon::TasInput { |
| 33 | class Tas; | 40 | class Tas; |
| 34 | } | 41 | } // namespace InputCommon::TasInput |
| 35 | 42 | ||
| 36 | namespace InputCommon { | 43 | namespace InputCommon { |
| 37 | namespace Polling { | 44 | namespace Polling { |
| 38 | 45 | /// Type of input desired for mapping purposes | |
| 39 | enum class DeviceType { Button, AnalogPreferred, Motion }; | 46 | enum class InputType { None, Button, Stick, Motion, Touch }; |
| 40 | |||
| 41 | /** | ||
| 42 | * A class that can be used to get inputs from an input device like controllers without having to | ||
| 43 | * poll the device's status yourself | ||
| 44 | */ | ||
| 45 | class DevicePoller { | ||
| 46 | public: | ||
| 47 | virtual ~DevicePoller() = default; | ||
| 48 | /// Setup and start polling for inputs, should be called before GetNextInput | ||
| 49 | /// If a device_id is provided, events should be filtered to only include events from this | ||
| 50 | /// device id | ||
| 51 | virtual void Start(const std::string& device_id = "") = 0; | ||
| 52 | /// Stop polling | ||
| 53 | virtual void Stop() = 0; | ||
| 54 | /** | ||
| 55 | * Every call to this function returns the next input recorded since calling Start | ||
| 56 | * @return A ParamPackage of the recorded input, which can be used to create an InputDevice. | ||
| 57 | * If there has been no input, the package is empty | ||
| 58 | */ | ||
| 59 | virtual Common::ParamPackage GetNextInput() = 0; | ||
| 60 | }; | ||
| 61 | } // namespace Polling | 47 | } // namespace Polling |
| 62 | 48 | ||
| 63 | class GCAnalogFactory; | ||
| 64 | class GCButtonFactory; | ||
| 65 | class UDPMotionFactory; | ||
| 66 | class UDPTouchFactory; | ||
| 67 | class MouseButtonFactory; | ||
| 68 | class MouseAnalogFactory; | ||
| 69 | class MouseMotionFactory; | ||
| 70 | class MouseTouchFactory; | ||
| 71 | class TasButtonFactory; | ||
| 72 | class TasAnalogFactory; | ||
| 73 | class Keyboard; | ||
| 74 | |||
| 75 | /** | 49 | /** |
| 76 | * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default | 50 | * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default |
| 77 | * mapping for the device. This is currently only implemented for the SDL backend devices. | 51 | * mapping for the device. |
| 78 | */ | 52 | */ |
| 79 | using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; | 53 | using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; |
| 80 | using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; | 54 | using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; |
| @@ -104,20 +78,27 @@ public: | |||
| 104 | [[nodiscard]] const Keyboard* GetKeyboard() const; | 78 | [[nodiscard]] const Keyboard* GetKeyboard() const; |
| 105 | 79 | ||
| 106 | /// Retrieves the underlying mouse device. | 80 | /// Retrieves the underlying mouse device. |
| 107 | [[nodiscard]] MouseInput::Mouse* GetMouse(); | 81 | [[nodiscard]] Mouse* GetMouse(); |
| 108 | 82 | ||
| 109 | /// Retrieves the underlying mouse device. | 83 | /// Retrieves the underlying mouse device. |
| 110 | [[nodiscard]] const MouseInput::Mouse* GetMouse() const; | 84 | [[nodiscard]] const Mouse* GetMouse() const; |
| 85 | |||
| 86 | /// Retrieves the underlying touch screen device. | ||
| 87 | [[nodiscard]] TouchScreen* GetTouchScreen(); | ||
| 111 | 88 | ||
| 112 | /// Retrieves the underlying tas device. | 89 | /// Retrieves the underlying touch screen device. |
| 90 | [[nodiscard]] const TouchScreen* GetTouchScreen() const; | ||
| 91 | |||
| 92 | /// Retrieves the underlying tas input device. | ||
| 113 | [[nodiscard]] TasInput::Tas* GetTas(); | 93 | [[nodiscard]] TasInput::Tas* GetTas(); |
| 114 | 94 | ||
| 115 | /// Retrieves the underlying tas device. | 95 | /// Retrieves the underlying tas input device. |
| 116 | [[nodiscard]] const TasInput::Tas* GetTas() const; | 96 | [[nodiscard]] const TasInput::Tas* GetTas() const; |
| 97 | |||
| 117 | /** | 98 | /** |
| 118 | * Returns all available input devices that this Factory can create a new device with. | 99 | * Returns all available input devices that this Factory can create a new device with. |
| 119 | * Each returned ParamPackage should have a `display` field used for display, a class field for | 100 | * Each returned ParamPackage should have a `display` field used for display, a `engine` field |
| 120 | * backends to determine if this backend is meant to service the request and any other | 101 | * for backends to determine if this backend is meant to service the request and any other |
| 121 | * information needed to identify this in the backend later. | 102 | * information needed to identify this in the backend later. |
| 122 | */ | 103 | */ |
| 123 | [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const; | 104 | [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const; |
| @@ -131,83 +112,34 @@ public: | |||
| 131 | /// Retrieves the motion mappings for the given device. | 112 | /// Retrieves the motion mappings for the given device. |
| 132 | [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const; | 113 | [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const; |
| 133 | 114 | ||
| 134 | /// Retrieves the underlying GameCube analog handler. | 115 | /// Returns an enum contaning the name to be displayed from the input engine. |
| 135 | [[nodiscard]] GCAnalogFactory* GetGCAnalogs(); | 116 | [[nodiscard]] Common::Input::ButtonNames GetButtonName( |
| 117 | const Common::ParamPackage& params) const; | ||
| 136 | 118 | ||
| 137 | /// Retrieves the underlying GameCube analog handler. | 119 | /// Returns true if device is a controller. |
| 138 | [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const; | 120 | [[nodiscard]] bool IsController(const Common::ParamPackage& params) const; |
| 139 | 121 | ||
| 140 | /// Retrieves the underlying GameCube button handler. | 122 | /// Reloads the input devices. |
| 141 | [[nodiscard]] GCButtonFactory* GetGCButtons(); | 123 | void ReloadInputDevices(); |
| 142 | |||
| 143 | /// Retrieves the underlying GameCube button handler. | ||
| 144 | [[nodiscard]] const GCButtonFactory* GetGCButtons() const; | ||
| 145 | |||
| 146 | /// Retrieves the underlying udp motion handler. | ||
| 147 | [[nodiscard]] UDPMotionFactory* GetUDPMotions(); | ||
| 148 | |||
| 149 | /// Retrieves the underlying udp motion handler. | ||
| 150 | [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const; | ||
| 151 | |||
| 152 | /// Retrieves the underlying udp touch handler. | ||
| 153 | [[nodiscard]] UDPTouchFactory* GetUDPTouch(); | ||
| 154 | |||
| 155 | /// Retrieves the underlying udp touch handler. | ||
| 156 | [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const; | ||
| 157 | |||
| 158 | /// Retrieves the underlying mouse button handler. | ||
| 159 | [[nodiscard]] MouseButtonFactory* GetMouseButtons(); | ||
| 160 | |||
| 161 | /// Retrieves the underlying mouse button handler. | ||
| 162 | [[nodiscard]] const MouseButtonFactory* GetMouseButtons() const; | ||
| 163 | |||
| 164 | /// Retrieves the underlying mouse analog handler. | ||
| 165 | [[nodiscard]] MouseAnalogFactory* GetMouseAnalogs(); | ||
| 166 | |||
| 167 | /// Retrieves the underlying mouse analog handler. | ||
| 168 | [[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const; | ||
| 169 | |||
| 170 | /// Retrieves the underlying mouse motion handler. | ||
| 171 | [[nodiscard]] MouseMotionFactory* GetMouseMotions(); | ||
| 172 | |||
| 173 | /// Retrieves the underlying mouse motion handler. | ||
| 174 | [[nodiscard]] const MouseMotionFactory* GetMouseMotions() const; | ||
| 175 | |||
| 176 | /// Retrieves the underlying mouse touch handler. | ||
| 177 | [[nodiscard]] MouseTouchFactory* GetMouseTouch(); | ||
| 178 | |||
| 179 | /// Retrieves the underlying mouse touch handler. | ||
| 180 | [[nodiscard]] const MouseTouchFactory* GetMouseTouch() const; | ||
| 181 | |||
| 182 | /// Retrieves the underlying tas button handler. | ||
| 183 | [[nodiscard]] TasButtonFactory* GetTasButtons(); | ||
| 184 | |||
| 185 | /// Retrieves the underlying tas button handler. | ||
| 186 | [[nodiscard]] const TasButtonFactory* GetTasButtons() const; | ||
| 187 | |||
| 188 | /// Retrieves the underlying tas analogs handler. | ||
| 189 | [[nodiscard]] TasAnalogFactory* GetTasAnalogs(); | ||
| 190 | 124 | ||
| 191 | /// Retrieves the underlying tas analogs handler. | 125 | /// Start polling from all backends for a desired input type. |
| 192 | [[nodiscard]] const TasAnalogFactory* GetTasAnalogs() const; | 126 | void BeginMapping(Polling::InputType type); |
| 193 | 127 | ||
| 194 | /// Reloads the input devices | 128 | /// Returns an input event with mapping information. |
| 195 | void ReloadInputDevices(); | 129 | [[nodiscard]] const Common::ParamPackage GetNextInput() const; |
| 196 | 130 | ||
| 197 | /// Get all DevicePoller from all backends for a specific device type | 131 | /// Stop polling from all backends. |
| 198 | [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers( | 132 | void StopMapping() const; |
| 199 | Polling::DeviceType type) const; | ||
| 200 | 133 | ||
| 201 | private: | 134 | private: |
| 202 | struct Impl; | 135 | struct Impl; |
| 203 | std::unique_ptr<Impl> impl; | 136 | std::unique_ptr<Impl> impl; |
| 204 | }; | 137 | }; |
| 205 | 138 | ||
| 206 | /// Generates a serialized param package for creating a keyboard button device | 139 | /// Generates a serialized param package for creating a keyboard button device. |
| 207 | std::string GenerateKeyboardParam(int key_code); | 140 | std::string GenerateKeyboardParam(int key_code); |
| 208 | 141 | ||
| 209 | /// Generates a serialized param package for creating an analog device taking input from keyboard | 142 | /// Generates a serialized param package for creating an analog device taking input from keyboard. |
| 210 | std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, | 143 | std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, |
| 211 | int key_modifier, float modifier_scale); | 144 | int key_modifier, float modifier_scale); |
| 212 | |||
| 213 | } // namespace InputCommon | 145 | } // namespace InputCommon |
diff --git a/src/input_common/motion_from_button.cpp b/src/input_common/motion_from_button.cpp deleted file mode 100644 index 29045a673..000000000 --- a/src/input_common/motion_from_button.cpp +++ /dev/null | |||
| @@ -1,34 +0,0 @@ | |||
| 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 "input_common/motion_from_button.h" | ||
| 6 | #include "input_common/motion_input.h" | ||
| 7 | |||
| 8 | namespace InputCommon { | ||
| 9 | |||
| 10 | class MotionKey final : public Input::MotionDevice { | ||
| 11 | public: | ||
| 12 | using Button = std::unique_ptr<Input::ButtonDevice>; | ||
| 13 | |||
| 14 | explicit MotionKey(Button key_) : key(std::move(key_)) {} | ||
| 15 | |||
| 16 | Input::MotionStatus GetStatus() const override { | ||
| 17 | |||
| 18 | if (key->GetStatus()) { | ||
| 19 | return motion.GetRandomMotion(2, 6); | ||
| 20 | } | ||
| 21 | return motion.GetRandomMotion(0, 0); | ||
| 22 | } | ||
| 23 | |||
| 24 | private: | ||
| 25 | Button key; | ||
| 26 | InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f}; | ||
| 27 | }; | ||
| 28 | |||
| 29 | std::unique_ptr<Input::MotionDevice> MotionFromButton::Create(const Common::ParamPackage& params) { | ||
| 30 | auto key = Input::CreateDevice<Input::ButtonDevice>(params.Serialize()); | ||
| 31 | return std::make_unique<MotionKey>(std::move(key)); | ||
| 32 | } | ||
| 33 | |||
| 34 | } // namespace InputCommon | ||
diff --git a/src/input_common/motion_from_button.h b/src/input_common/motion_from_button.h deleted file mode 100644 index a959046fb..000000000 --- a/src/input_common/motion_from_button.h +++ /dev/null | |||
| @@ -1,25 +0,0 @@ | |||
| 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 "core/frontend/input.h" | ||
| 8 | |||
| 9 | namespace InputCommon { | ||
| 10 | |||
| 11 | /** | ||
| 12 | * An motion device factory that takes a keyboard button and uses it as a random | ||
| 13 | * motion device. | ||
| 14 | */ | ||
| 15 | class MotionFromButton final : public Input::Factory<Input::MotionDevice> { | ||
| 16 | public: | ||
| 17 | /** | ||
| 18 | * Creates an motion device from button devices | ||
| 19 | * @param params contains parameters for creating the device: | ||
| 20 | * - "key": a serialized ParamPackage for creating a button device | ||
| 21 | */ | ||
| 22 | std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override; | ||
| 23 | }; | ||
| 24 | |||
| 25 | } // namespace InputCommon | ||
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp deleted file mode 100644 index 3b052ffb2..000000000 --- a/src/input_common/mouse/mouse_input.cpp +++ /dev/null | |||
| @@ -1,223 +0,0 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <stop_token> | ||
| 6 | #include <thread> | ||
| 7 | |||
| 8 | #include "common/settings.h" | ||
| 9 | #include "input_common/mouse/mouse_input.h" | ||
| 10 | |||
| 11 | namespace MouseInput { | ||
| 12 | |||
| 13 | Mouse::Mouse() { | ||
| 14 | update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); }); | ||
| 15 | } | ||
| 16 | |||
| 17 | Mouse::~Mouse() = default; | ||
| 18 | |||
| 19 | void Mouse::UpdateThread(std::stop_token stop_token) { | ||
| 20 | constexpr int update_time = 10; | ||
| 21 | while (!stop_token.stop_requested()) { | ||
| 22 | for (MouseInfo& info : mouse_info) { | ||
| 23 | const Common::Vec3f angular_direction{ | ||
| 24 | -info.tilt_direction.y, | ||
| 25 | 0.0f, | ||
| 26 | -info.tilt_direction.x, | ||
| 27 | }; | ||
| 28 | |||
| 29 | info.motion.SetGyroscope(angular_direction * info.tilt_speed); | ||
| 30 | info.motion.UpdateRotation(update_time * 1000); | ||
| 31 | info.motion.UpdateOrientation(update_time * 1000); | ||
| 32 | info.tilt_speed = 0; | ||
| 33 | info.data.motion = info.motion.GetMotion(); | ||
| 34 | if (Settings::values.mouse_panning) { | ||
| 35 | info.last_mouse_change *= 0.96f; | ||
| 36 | info.data.axis = {static_cast<int>(16 * info.last_mouse_change.x), | ||
| 37 | static_cast<int>(16 * -info.last_mouse_change.y)}; | ||
| 38 | } | ||
| 39 | } | ||
| 40 | if (configuring) { | ||
| 41 | UpdateYuzuSettings(); | ||
| 42 | } | ||
| 43 | if (mouse_panning_timout++ > 20) { | ||
| 44 | StopPanning(); | ||
| 45 | } | ||
| 46 | std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void Mouse::UpdateYuzuSettings() { | ||
| 51 | if (buttons == 0) { | ||
| 52 | return; | ||
| 53 | } | ||
| 54 | |||
| 55 | mouse_queue.Push(MouseStatus{ | ||
| 56 | .button = last_button, | ||
| 57 | }); | ||
| 58 | } | ||
| 59 | |||
| 60 | void Mouse::PressButton(int x, int y, MouseButton button_) { | ||
| 61 | const auto button_index = static_cast<std::size_t>(button_); | ||
| 62 | if (button_index >= mouse_info.size()) { | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | const auto button = 1U << button_index; | ||
| 67 | buttons |= static_cast<u16>(button); | ||
| 68 | last_button = button_; | ||
| 69 | |||
| 70 | mouse_info[button_index].mouse_origin = Common::MakeVec(x, y); | ||
| 71 | mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y); | ||
| 72 | mouse_info[button_index].data.pressed = true; | ||
| 73 | } | ||
| 74 | |||
| 75 | void Mouse::StopPanning() { | ||
| 76 | for (MouseInfo& info : mouse_info) { | ||
| 77 | if (Settings::values.mouse_panning) { | ||
| 78 | info.data.axis = {}; | ||
| 79 | info.tilt_speed = 0; | ||
| 80 | info.last_mouse_change = {}; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | void Mouse::MouseMove(int x, int y, int center_x, int center_y) { | ||
| 86 | for (MouseInfo& info : mouse_info) { | ||
| 87 | if (Settings::values.mouse_panning) { | ||
| 88 | auto mouse_change = | ||
| 89 | (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); | ||
| 90 | mouse_panning_timout = 0; | ||
| 91 | |||
| 92 | if (mouse_change.y == 0 && mouse_change.x == 0) { | ||
| 93 | continue; | ||
| 94 | } | ||
| 95 | const auto mouse_change_length = mouse_change.Length(); | ||
| 96 | if (mouse_change_length < 3.0f) { | ||
| 97 | mouse_change /= mouse_change_length / 3.0f; | ||
| 98 | } | ||
| 99 | |||
| 100 | info.last_mouse_change = (info.last_mouse_change * 0.91f) + (mouse_change * 0.09f); | ||
| 101 | |||
| 102 | const auto last_mouse_change_length = info.last_mouse_change.Length(); | ||
| 103 | if (last_mouse_change_length > 8.0f) { | ||
| 104 | info.last_mouse_change /= last_mouse_change_length / 8.0f; | ||
| 105 | } else if (last_mouse_change_length < 1.0f) { | ||
| 106 | info.last_mouse_change = mouse_change / mouse_change.Length(); | ||
| 107 | } | ||
| 108 | |||
| 109 | info.tilt_direction = info.last_mouse_change; | ||
| 110 | info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity; | ||
| 111 | continue; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (info.data.pressed) { | ||
| 115 | const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin; | ||
| 116 | const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position; | ||
| 117 | info.last_mouse_position = Common::MakeVec(x, y); | ||
| 118 | info.data.axis = {mouse_move.x, -mouse_move.y}; | ||
| 119 | |||
| 120 | if (mouse_change.x == 0 && mouse_change.y == 0) { | ||
| 121 | info.tilt_speed = 0; | ||
| 122 | } else { | ||
| 123 | info.tilt_direction = mouse_change.Cast<float>(); | ||
| 124 | info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | void Mouse::ReleaseButton(MouseButton button_) { | ||
| 131 | const auto button_index = static_cast<std::size_t>(button_); | ||
| 132 | if (button_index >= mouse_info.size()) { | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | |||
| 136 | const auto button = 1U << button_index; | ||
| 137 | buttons &= static_cast<u16>(0xFF - button); | ||
| 138 | |||
| 139 | mouse_info[button_index].tilt_speed = 0; | ||
| 140 | mouse_info[button_index].data.pressed = false; | ||
| 141 | mouse_info[button_index].data.axis = {0, 0}; | ||
| 142 | } | ||
| 143 | |||
| 144 | void Mouse::ReleaseAllButtons() { | ||
| 145 | buttons = 0; | ||
| 146 | for (auto& info : mouse_info) { | ||
| 147 | info.tilt_speed = 0; | ||
| 148 | info.data.pressed = false; | ||
| 149 | info.data.axis = {0, 0}; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | void Mouse::BeginConfiguration() { | ||
| 154 | buttons = 0; | ||
| 155 | last_button = MouseButton::Undefined; | ||
| 156 | mouse_queue.Clear(); | ||
| 157 | configuring = true; | ||
| 158 | } | ||
| 159 | |||
| 160 | void Mouse::EndConfiguration() { | ||
| 161 | buttons = 0; | ||
| 162 | for (MouseInfo& info : mouse_info) { | ||
| 163 | info.tilt_speed = 0; | ||
| 164 | info.data.pressed = false; | ||
| 165 | info.data.axis = {0, 0}; | ||
| 166 | } | ||
| 167 | last_button = MouseButton::Undefined; | ||
| 168 | mouse_queue.Clear(); | ||
| 169 | configuring = false; | ||
| 170 | } | ||
| 171 | |||
| 172 | bool Mouse::ToggleButton(std::size_t button_) { | ||
| 173 | if (button_ >= mouse_info.size()) { | ||
| 174 | return false; | ||
| 175 | } | ||
| 176 | const auto button = 1U << button_; | ||
| 177 | const bool button_state = (toggle_buttons & button) != 0; | ||
| 178 | const bool button_lock = (lock_buttons & button) != 0; | ||
| 179 | |||
| 180 | if (button_lock) { | ||
| 181 | return button_state; | ||
| 182 | } | ||
| 183 | |||
| 184 | lock_buttons |= static_cast<u16>(button); | ||
| 185 | |||
| 186 | if (button_state) { | ||
| 187 | toggle_buttons &= static_cast<u16>(0xFF - button); | ||
| 188 | } else { | ||
| 189 | toggle_buttons |= static_cast<u16>(button); | ||
| 190 | } | ||
| 191 | |||
| 192 | return !button_state; | ||
| 193 | } | ||
| 194 | |||
| 195 | bool Mouse::UnlockButton(std::size_t button_) { | ||
| 196 | if (button_ >= mouse_info.size()) { | ||
| 197 | return false; | ||
| 198 | } | ||
| 199 | |||
| 200 | const auto button = 1U << button_; | ||
| 201 | const bool button_state = (toggle_buttons & button) != 0; | ||
| 202 | |||
| 203 | lock_buttons &= static_cast<u16>(0xFF - button); | ||
| 204 | |||
| 205 | return button_state; | ||
| 206 | } | ||
| 207 | |||
| 208 | Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() { | ||
| 209 | return mouse_queue; | ||
| 210 | } | ||
| 211 | |||
| 212 | const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const { | ||
| 213 | return mouse_queue; | ||
| 214 | } | ||
| 215 | |||
| 216 | MouseData& Mouse::GetMouseState(std::size_t button) { | ||
| 217 | return mouse_info[button].data; | ||
| 218 | } | ||
| 219 | |||
| 220 | const MouseData& Mouse::GetMouseState(std::size_t button) const { | ||
| 221 | return mouse_info[button].data; | ||
| 222 | } | ||
| 223 | } // namespace MouseInput | ||
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h deleted file mode 100644 index c8bae99c1..000000000 --- a/src/input_common/mouse/mouse_input.h +++ /dev/null | |||
| @@ -1,116 +0,0 @@ | |||
| 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 | #include <mutex> | ||
| 9 | #include <stop_token> | ||
| 10 | #include <thread> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/threadsafe_queue.h" | ||
| 14 | #include "common/vector_math.h" | ||
| 15 | #include "core/frontend/input.h" | ||
| 16 | #include "input_common/motion_input.h" | ||
| 17 | |||
| 18 | namespace MouseInput { | ||
| 19 | |||
| 20 | enum class MouseButton { | ||
| 21 | Left, | ||
| 22 | Right, | ||
| 23 | Wheel, | ||
| 24 | Backward, | ||
| 25 | Forward, | ||
| 26 | Task, | ||
| 27 | Extra, | ||
| 28 | Undefined, | ||
| 29 | }; | ||
| 30 | |||
| 31 | struct MouseStatus { | ||
| 32 | MouseButton button{MouseButton::Undefined}; | ||
| 33 | }; | ||
| 34 | |||
| 35 | struct MouseData { | ||
| 36 | bool pressed{}; | ||
| 37 | std::array<int, 2> axis{}; | ||
| 38 | Input::MotionStatus motion{}; | ||
| 39 | Input::TouchStatus touch{}; | ||
| 40 | }; | ||
| 41 | |||
| 42 | class Mouse { | ||
| 43 | public: | ||
| 44 | Mouse(); | ||
| 45 | ~Mouse(); | ||
| 46 | |||
| 47 | /// Used for polling | ||
| 48 | void BeginConfiguration(); | ||
| 49 | void EndConfiguration(); | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Signals that a button is pressed. | ||
| 53 | * @param x the x-coordinate of the cursor | ||
| 54 | * @param y the y-coordinate of the cursor | ||
| 55 | * @param button_ the button pressed | ||
| 56 | */ | ||
| 57 | void PressButton(int x, int y, MouseButton button_); | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Signals that mouse has moved. | ||
| 61 | * @param x the x-coordinate of the cursor | ||
| 62 | * @param y the y-coordinate of the cursor | ||
| 63 | * @param center_x the x-coordinate of the middle of the screen | ||
| 64 | * @param center_y the y-coordinate of the middle of the screen | ||
| 65 | */ | ||
| 66 | void MouseMove(int x, int y, int center_x, int center_y); | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Signals that a button is released. | ||
| 70 | * @param button_ the button pressed | ||
| 71 | */ | ||
| 72 | void ReleaseButton(MouseButton button_); | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Signals that all buttons are released | ||
| 76 | */ | ||
| 77 | void ReleaseAllButtons(); | ||
| 78 | |||
| 79 | [[nodiscard]] bool ToggleButton(std::size_t button_); | ||
| 80 | [[nodiscard]] bool UnlockButton(std::size_t button_); | ||
| 81 | |||
| 82 | [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue(); | ||
| 83 | [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const; | ||
| 84 | |||
| 85 | [[nodiscard]] MouseData& GetMouseState(std::size_t button); | ||
| 86 | [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const; | ||
| 87 | |||
| 88 | private: | ||
| 89 | void UpdateThread(std::stop_token stop_token); | ||
| 90 | void UpdateYuzuSettings(); | ||
| 91 | void StopPanning(); | ||
| 92 | |||
| 93 | struct MouseInfo { | ||
| 94 | InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f}; | ||
| 95 | Common::Vec2<int> mouse_origin; | ||
| 96 | Common::Vec2<int> last_mouse_position; | ||
| 97 | Common::Vec2<float> last_mouse_change; | ||
| 98 | bool is_tilting = false; | ||
| 99 | float sensitivity{0.120f}; | ||
| 100 | |||
| 101 | float tilt_speed = 0; | ||
| 102 | Common::Vec2<float> tilt_direction; | ||
| 103 | MouseData data; | ||
| 104 | }; | ||
| 105 | |||
| 106 | u16 buttons{}; | ||
| 107 | u16 toggle_buttons{}; | ||
| 108 | u16 lock_buttons{}; | ||
| 109 | std::jthread update_thread; | ||
| 110 | MouseButton last_button{MouseButton::Undefined}; | ||
| 111 | std::array<MouseInfo, 7> mouse_info; | ||
| 112 | Common::SPSCQueue<MouseStatus> mouse_queue; | ||
| 113 | bool configuring{false}; | ||
| 114 | int mouse_panning_timout{}; | ||
| 115 | }; | ||
| 116 | } // namespace MouseInput | ||
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp deleted file mode 100644 index 090b26972..000000000 --- a/src/input_common/mouse/mouse_poller.cpp +++ /dev/null | |||
| @@ -1,299 +0,0 @@ | |||
| 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 <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include <mutex> | ||
| 8 | #include <utility> | ||
| 9 | |||
| 10 | #include "common/settings.h" | ||
| 11 | #include "common/threadsafe_queue.h" | ||
| 12 | #include "input_common/mouse/mouse_input.h" | ||
| 13 | #include "input_common/mouse/mouse_poller.h" | ||
| 14 | |||
| 15 | namespace InputCommon { | ||
| 16 | |||
| 17 | class MouseButton final : public Input::ButtonDevice { | ||
| 18 | public: | ||
| 19 | explicit MouseButton(u32 button_, bool toggle_, MouseInput::Mouse* mouse_input_) | ||
| 20 | : button(button_), toggle(toggle_), mouse_input(mouse_input_) {} | ||
| 21 | |||
| 22 | bool GetStatus() const override { | ||
| 23 | const bool button_state = mouse_input->GetMouseState(button).pressed; | ||
| 24 | if (!toggle) { | ||
| 25 | return button_state; | ||
| 26 | } | ||
| 27 | |||
| 28 | if (button_state) { | ||
| 29 | return mouse_input->ToggleButton(button); | ||
| 30 | } | ||
| 31 | return mouse_input->UnlockButton(button); | ||
| 32 | } | ||
| 33 | |||
| 34 | private: | ||
| 35 | const u32 button; | ||
| 36 | const bool toggle; | ||
| 37 | MouseInput::Mouse* mouse_input; | ||
| 38 | }; | ||
| 39 | |||
| 40 | MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) | ||
| 41 | : mouse_input(std::move(mouse_input_)) {} | ||
| 42 | |||
| 43 | std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create( | ||
| 44 | const Common::ParamPackage& params) { | ||
| 45 | const auto button_id = params.Get("button", 0); | ||
| 46 | const auto toggle = params.Get("toggle", false); | ||
| 47 | |||
| 48 | return std::make_unique<MouseButton>(button_id, toggle, mouse_input.get()); | ||
| 49 | } | ||
| 50 | |||
| 51 | Common::ParamPackage MouseButtonFactory::GetNextInput() const { | ||
| 52 | MouseInput::MouseStatus pad; | ||
| 53 | Common::ParamPackage params; | ||
| 54 | auto& queue = mouse_input->GetMouseQueue(); | ||
| 55 | while (queue.Pop(pad)) { | ||
| 56 | // This while loop will break on the earliest detected button | ||
| 57 | if (pad.button != MouseInput::MouseButton::Undefined) { | ||
| 58 | params.Set("engine", "mouse"); | ||
| 59 | params.Set("button", static_cast<u16>(pad.button)); | ||
| 60 | params.Set("toggle", false); | ||
| 61 | return params; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | return params; | ||
| 65 | } | ||
| 66 | |||
| 67 | void MouseButtonFactory::BeginConfiguration() { | ||
| 68 | polling = true; | ||
| 69 | mouse_input->BeginConfiguration(); | ||
| 70 | } | ||
| 71 | |||
| 72 | void MouseButtonFactory::EndConfiguration() { | ||
| 73 | polling = false; | ||
| 74 | mouse_input->EndConfiguration(); | ||
| 75 | } | ||
| 76 | |||
| 77 | class MouseAnalog final : public Input::AnalogDevice { | ||
| 78 | public: | ||
| 79 | explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_, | ||
| 80 | float deadzone_, float range_, const MouseInput::Mouse* mouse_input_) | ||
| 81 | : button(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_), | ||
| 82 | deadzone(deadzone_), range(range_), mouse_input(mouse_input_) {} | ||
| 83 | |||
| 84 | float GetAxis(u32 axis) const { | ||
| 85 | std::lock_guard lock{mutex}; | ||
| 86 | const auto axis_value = | ||
| 87 | static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis)); | ||
| 88 | const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.10f; | ||
| 89 | return axis_value * sensitivity / (100.0f * range); | ||
| 90 | } | ||
| 91 | |||
| 92 | std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { | ||
| 93 | float x = GetAxis(analog_axis_x); | ||
| 94 | float y = GetAxis(analog_axis_y); | ||
| 95 | if (invert_x) { | ||
| 96 | x = -x; | ||
| 97 | } | ||
| 98 | if (invert_y) { | ||
| 99 | y = -y; | ||
| 100 | } | ||
| 101 | |||
| 102 | // Make sure the coordinates are in the unit circle, | ||
| 103 | // otherwise normalize it. | ||
| 104 | float r = x * x + y * y; | ||
| 105 | if (r > 1.0f) { | ||
| 106 | r = std::sqrt(r); | ||
| 107 | x /= r; | ||
| 108 | y /= r; | ||
| 109 | } | ||
| 110 | |||
| 111 | return {x, y}; | ||
| 112 | } | ||
| 113 | |||
| 114 | std::tuple<float, float> GetStatus() const override { | ||
| 115 | const auto [x, y] = GetAnalog(axis_x, axis_y); | ||
| 116 | const float r = std::sqrt((x * x) + (y * y)); | ||
| 117 | if (r > deadzone) { | ||
| 118 | return {x / r * (r - deadzone) / (1 - deadzone), | ||
| 119 | y / r * (r - deadzone) / (1 - deadzone)}; | ||
| 120 | } | ||
| 121 | return {0.0f, 0.0f}; | ||
| 122 | } | ||
| 123 | |||
| 124 | std::tuple<float, float> GetRawStatus() const override { | ||
| 125 | const float x = GetAxis(axis_x); | ||
| 126 | const float y = GetAxis(axis_y); | ||
| 127 | return {x, y}; | ||
| 128 | } | ||
| 129 | |||
| 130 | Input::AnalogProperties GetAnalogProperties() const override { | ||
| 131 | return {deadzone, range, 0.5f}; | ||
| 132 | } | ||
| 133 | |||
| 134 | private: | ||
| 135 | const u32 button; | ||
| 136 | const u32 axis_x; | ||
| 137 | const u32 axis_y; | ||
| 138 | const bool invert_x; | ||
| 139 | const bool invert_y; | ||
| 140 | const float deadzone; | ||
| 141 | const float range; | ||
| 142 | const MouseInput::Mouse* mouse_input; | ||
| 143 | mutable std::mutex mutex; | ||
| 144 | }; | ||
| 145 | |||
| 146 | /// An analog device factory that creates analog devices from GC Adapter | ||
| 147 | MouseAnalogFactory::MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) | ||
| 148 | : mouse_input(std::move(mouse_input_)) {} | ||
| 149 | |||
| 150 | /** | ||
| 151 | * Creates analog device from joystick axes | ||
| 152 | * @param params contains parameters for creating the device: | ||
| 153 | * - "port": the nth gcpad on the adapter | ||
| 154 | * - "axis_x": the index of the axis to be bind as x-axis | ||
| 155 | * - "axis_y": the index of the axis to be bind as y-axis | ||
| 156 | */ | ||
| 157 | std::unique_ptr<Input::AnalogDevice> MouseAnalogFactory::Create( | ||
| 158 | const Common::ParamPackage& params) { | ||
| 159 | const auto port = static_cast<u32>(params.Get("port", 0)); | ||
| 160 | const auto axis_x = static_cast<u32>(params.Get("axis_x", 0)); | ||
| 161 | const auto axis_y = static_cast<u32>(params.Get("axis_y", 1)); | ||
| 162 | const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); | ||
| 163 | const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); | ||
| 164 | const std::string invert_x_value = params.Get("invert_x", "+"); | ||
| 165 | const std::string invert_y_value = params.Get("invert_y", "+"); | ||
| 166 | const bool invert_x = invert_x_value == "-"; | ||
| 167 | const bool invert_y = invert_y_value == "-"; | ||
| 168 | |||
| 169 | return std::make_unique<MouseAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range, | ||
| 170 | mouse_input.get()); | ||
| 171 | } | ||
| 172 | |||
| 173 | void MouseAnalogFactory::BeginConfiguration() { | ||
| 174 | polling = true; | ||
| 175 | mouse_input->BeginConfiguration(); | ||
| 176 | } | ||
| 177 | |||
| 178 | void MouseAnalogFactory::EndConfiguration() { | ||
| 179 | polling = false; | ||
| 180 | mouse_input->EndConfiguration(); | ||
| 181 | } | ||
| 182 | |||
| 183 | Common::ParamPackage MouseAnalogFactory::GetNextInput() const { | ||
| 184 | MouseInput::MouseStatus pad; | ||
| 185 | Common::ParamPackage params; | ||
| 186 | auto& queue = mouse_input->GetMouseQueue(); | ||
| 187 | while (queue.Pop(pad)) { | ||
| 188 | // This while loop will break on the earliest detected button | ||
| 189 | if (pad.button != MouseInput::MouseButton::Undefined) { | ||
| 190 | params.Set("engine", "mouse"); | ||
| 191 | params.Set("port", static_cast<u16>(pad.button)); | ||
| 192 | params.Set("axis_x", 0); | ||
| 193 | params.Set("axis_y", 1); | ||
| 194 | params.Set("invert_x", "+"); | ||
| 195 | params.Set("invert_y", "+"); | ||
| 196 | return params; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | return params; | ||
| 200 | } | ||
| 201 | |||
| 202 | class MouseMotion final : public Input::MotionDevice { | ||
| 203 | public: | ||
| 204 | explicit MouseMotion(u32 button_, const MouseInput::Mouse* mouse_input_) | ||
| 205 | : button(button_), mouse_input(mouse_input_) {} | ||
| 206 | |||
| 207 | Input::MotionStatus GetStatus() const override { | ||
| 208 | return mouse_input->GetMouseState(button).motion; | ||
| 209 | } | ||
| 210 | |||
| 211 | private: | ||
| 212 | const u32 button; | ||
| 213 | const MouseInput::Mouse* mouse_input; | ||
| 214 | }; | ||
| 215 | |||
| 216 | MouseMotionFactory::MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) | ||
| 217 | : mouse_input(std::move(mouse_input_)) {} | ||
| 218 | |||
| 219 | std::unique_ptr<Input::MotionDevice> MouseMotionFactory::Create( | ||
| 220 | const Common::ParamPackage& params) { | ||
| 221 | const auto button_id = params.Get("button", 0); | ||
| 222 | |||
| 223 | return std::make_unique<MouseMotion>(button_id, mouse_input.get()); | ||
| 224 | } | ||
| 225 | |||
| 226 | Common::ParamPackage MouseMotionFactory::GetNextInput() const { | ||
| 227 | MouseInput::MouseStatus pad; | ||
| 228 | Common::ParamPackage params; | ||
| 229 | auto& queue = mouse_input->GetMouseQueue(); | ||
| 230 | while (queue.Pop(pad)) { | ||
| 231 | // This while loop will break on the earliest detected button | ||
| 232 | if (pad.button != MouseInput::MouseButton::Undefined) { | ||
| 233 | params.Set("engine", "mouse"); | ||
| 234 | params.Set("button", static_cast<u16>(pad.button)); | ||
| 235 | return params; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | return params; | ||
| 239 | } | ||
| 240 | |||
| 241 | void MouseMotionFactory::BeginConfiguration() { | ||
| 242 | polling = true; | ||
| 243 | mouse_input->BeginConfiguration(); | ||
| 244 | } | ||
| 245 | |||
| 246 | void MouseMotionFactory::EndConfiguration() { | ||
| 247 | polling = false; | ||
| 248 | mouse_input->EndConfiguration(); | ||
| 249 | } | ||
| 250 | |||
| 251 | class MouseTouch final : public Input::TouchDevice { | ||
| 252 | public: | ||
| 253 | explicit MouseTouch(u32 button_, const MouseInput::Mouse* mouse_input_) | ||
| 254 | : button(button_), mouse_input(mouse_input_) {} | ||
| 255 | |||
| 256 | Input::TouchStatus GetStatus() const override { | ||
| 257 | return mouse_input->GetMouseState(button).touch; | ||
| 258 | } | ||
| 259 | |||
| 260 | private: | ||
| 261 | const u32 button; | ||
| 262 | const MouseInput::Mouse* mouse_input; | ||
| 263 | }; | ||
| 264 | |||
| 265 | MouseTouchFactory::MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) | ||
| 266 | : mouse_input(std::move(mouse_input_)) {} | ||
| 267 | |||
| 268 | std::unique_ptr<Input::TouchDevice> MouseTouchFactory::Create(const Common::ParamPackage& params) { | ||
| 269 | const auto button_id = params.Get("button", 0); | ||
| 270 | |||
| 271 | return std::make_unique<MouseTouch>(button_id, mouse_input.get()); | ||
| 272 | } | ||
| 273 | |||
| 274 | Common::ParamPackage MouseTouchFactory::GetNextInput() const { | ||
| 275 | MouseInput::MouseStatus pad; | ||
| 276 | Common::ParamPackage params; | ||
| 277 | auto& queue = mouse_input->GetMouseQueue(); | ||
| 278 | while (queue.Pop(pad)) { | ||
| 279 | // This while loop will break on the earliest detected button | ||
| 280 | if (pad.button != MouseInput::MouseButton::Undefined) { | ||
| 281 | params.Set("engine", "mouse"); | ||
| 282 | params.Set("button", static_cast<u16>(pad.button)); | ||
| 283 | return params; | ||
| 284 | } | ||
| 285 | } | ||
| 286 | return params; | ||
| 287 | } | ||
| 288 | |||
| 289 | void MouseTouchFactory::BeginConfiguration() { | ||
| 290 | polling = true; | ||
| 291 | mouse_input->BeginConfiguration(); | ||
| 292 | } | ||
| 293 | |||
| 294 | void MouseTouchFactory::EndConfiguration() { | ||
| 295 | polling = false; | ||
| 296 | mouse_input->EndConfiguration(); | ||
| 297 | } | ||
| 298 | |||
| 299 | } // namespace InputCommon | ||
diff --git a/src/input_common/mouse/mouse_poller.h b/src/input_common/mouse/mouse_poller.h deleted file mode 100644 index cf331293b..000000000 --- a/src/input_common/mouse/mouse_poller.h +++ /dev/null | |||
| @@ -1,109 +0,0 @@ | |||
| 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 <memory> | ||
| 8 | #include "core/frontend/input.h" | ||
| 9 | #include "input_common/mouse/mouse_input.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * A button device factory representing a mouse. It receives mouse events and forward them | ||
| 15 | * to all button devices it created. | ||
| 16 | */ | ||
| 17 | class MouseButtonFactory final : public Input::Factory<Input::ButtonDevice> { | ||
| 18 | public: | ||
| 19 | explicit MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_); | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Creates a button device from a button press | ||
| 23 | * @param params contains parameters for creating the device: | ||
| 24 | * - "code": the code of the key to bind with the button | ||
| 25 | */ | ||
| 26 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; | ||
| 27 | |||
| 28 | Common::ParamPackage GetNextInput() const; | ||
| 29 | |||
| 30 | /// For device input configuration/polling | ||
| 31 | void BeginConfiguration(); | ||
| 32 | void EndConfiguration(); | ||
| 33 | |||
| 34 | bool IsPolling() const { | ||
| 35 | return polling; | ||
| 36 | } | ||
| 37 | |||
| 38 | private: | ||
| 39 | std::shared_ptr<MouseInput::Mouse> mouse_input; | ||
| 40 | bool polling = false; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /// An analog device factory that creates analog devices from mouse | ||
| 44 | class MouseAnalogFactory final : public Input::Factory<Input::AnalogDevice> { | ||
| 45 | public: | ||
| 46 | explicit MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_); | ||
| 47 | |||
| 48 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; | ||
| 49 | |||
| 50 | Common::ParamPackage GetNextInput() const; | ||
| 51 | |||
| 52 | /// For device input configuration/polling | ||
| 53 | void BeginConfiguration(); | ||
| 54 | void EndConfiguration(); | ||
| 55 | |||
| 56 | bool IsPolling() const { | ||
| 57 | return polling; | ||
| 58 | } | ||
| 59 | |||
| 60 | private: | ||
| 61 | std::shared_ptr<MouseInput::Mouse> mouse_input; | ||
| 62 | bool polling = false; | ||
| 63 | }; | ||
| 64 | |||
| 65 | /// A motion device factory that creates motion devices from mouse | ||
| 66 | class MouseMotionFactory final : public Input::Factory<Input::MotionDevice> { | ||
| 67 | public: | ||
| 68 | explicit MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_); | ||
| 69 | |||
| 70 | std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override; | ||
| 71 | |||
| 72 | Common::ParamPackage GetNextInput() const; | ||
| 73 | |||
| 74 | /// For device input configuration/polling | ||
| 75 | void BeginConfiguration(); | ||
| 76 | void EndConfiguration(); | ||
| 77 | |||
| 78 | bool IsPolling() const { | ||
| 79 | return polling; | ||
| 80 | } | ||
| 81 | |||
| 82 | private: | ||
| 83 | std::shared_ptr<MouseInput::Mouse> mouse_input; | ||
| 84 | bool polling = false; | ||
| 85 | }; | ||
| 86 | |||
| 87 | /// An touch device factory that creates touch devices from mouse | ||
| 88 | class MouseTouchFactory final : public Input::Factory<Input::TouchDevice> { | ||
| 89 | public: | ||
| 90 | explicit MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_); | ||
| 91 | |||
| 92 | std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override; | ||
| 93 | |||
| 94 | Common::ParamPackage GetNextInput() const; | ||
| 95 | |||
| 96 | /// For device input configuration/polling | ||
| 97 | void BeginConfiguration(); | ||
| 98 | void EndConfiguration(); | ||
| 99 | |||
| 100 | bool IsPolling() const { | ||
| 101 | return polling; | ||
| 102 | } | ||
| 103 | |||
| 104 | private: | ||
| 105 | std::shared_ptr<MouseInput::Mouse> mouse_input; | ||
| 106 | bool polling = false; | ||
| 107 | }; | ||
| 108 | |||
| 109 | } // namespace InputCommon | ||
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp deleted file mode 100644 index 644db3448..000000000 --- a/src/input_common/sdl/sdl.cpp +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "input_common/sdl/sdl.h" | ||
| 6 | #ifdef HAVE_SDL2 | ||
| 7 | #include "input_common/sdl/sdl_impl.h" | ||
| 8 | #endif | ||
| 9 | |||
| 10 | namespace InputCommon::SDL { | ||
| 11 | |||
| 12 | std::unique_ptr<State> Init() { | ||
| 13 | #ifdef HAVE_SDL2 | ||
| 14 | return std::make_unique<SDLState>(); | ||
| 15 | #else | ||
| 16 | return std::make_unique<NullState>(); | ||
| 17 | #endif | ||
| 18 | } | ||
| 19 | } // namespace InputCommon::SDL | ||
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h deleted file mode 100644 index b5d41bba4..000000000 --- a/src/input_common/sdl/sdl.h +++ /dev/null | |||
| @@ -1,51 +0,0 @@ | |||
| 1 | // Copyright 2018 Citra 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 | #include <vector> | ||
| 9 | #include "common/param_package.h" | ||
| 10 | #include "input_common/main.h" | ||
| 11 | |||
| 12 | namespace InputCommon::Polling { | ||
| 13 | class DevicePoller; | ||
| 14 | enum class DeviceType; | ||
| 15 | } // namespace InputCommon::Polling | ||
| 16 | |||
| 17 | namespace InputCommon::SDL { | ||
| 18 | |||
| 19 | class State { | ||
| 20 | public: | ||
| 21 | using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>; | ||
| 22 | |||
| 23 | /// Unregisters SDL device factories and shut them down. | ||
| 24 | virtual ~State() = default; | ||
| 25 | |||
| 26 | virtual Pollers GetPollers(Polling::DeviceType) { | ||
| 27 | return {}; | ||
| 28 | } | ||
| 29 | |||
| 30 | virtual std::vector<Common::ParamPackage> GetInputDevices() { | ||
| 31 | return {}; | ||
| 32 | } | ||
| 33 | |||
| 34 | virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) { | ||
| 35 | return {}; | ||
| 36 | } | ||
| 37 | virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) { | ||
| 38 | return {}; | ||
| 39 | } | ||
| 40 | virtual MotionMapping GetMotionMappingForDevice(const Common::ParamPackage&) { | ||
| 41 | return {}; | ||
| 42 | } | ||
| 43 | }; | ||
| 44 | |||
| 45 | class NullState : public State { | ||
| 46 | public: | ||
| 47 | }; | ||
| 48 | |||
| 49 | std::unique_ptr<State> Init(); | ||
| 50 | |||
| 51 | } // namespace InputCommon::SDL | ||
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp deleted file mode 100644 index ecb00d428..000000000 --- a/src/input_common/sdl/sdl_impl.cpp +++ /dev/null | |||
| @@ -1,1658 +0,0 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <atomic> | ||
| 8 | #include <chrono> | ||
| 9 | #include <cmath> | ||
| 10 | #include <functional> | ||
| 11 | #include <mutex> | ||
| 12 | #include <optional> | ||
| 13 | #include <sstream> | ||
| 14 | #include <string> | ||
| 15 | #include <thread> | ||
| 16 | #include <tuple> | ||
| 17 | #include <unordered_map> | ||
| 18 | #include <utility> | ||
| 19 | #include <vector> | ||
| 20 | |||
| 21 | #include "common/logging/log.h" | ||
| 22 | #include "common/math_util.h" | ||
| 23 | #include "common/param_package.h" | ||
| 24 | #include "common/settings.h" | ||
| 25 | #include "common/threadsafe_queue.h" | ||
| 26 | #include "core/frontend/input.h" | ||
| 27 | #include "input_common/motion_input.h" | ||
| 28 | #include "input_common/sdl/sdl_impl.h" | ||
| 29 | |||
| 30 | namespace InputCommon::SDL { | ||
| 31 | |||
| 32 | namespace { | ||
| 33 | std::string GetGUID(SDL_Joystick* joystick) { | ||
| 34 | const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); | ||
| 35 | char guid_str[33]; | ||
| 36 | SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); | ||
| 37 | return guid_str; | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice | ||
| 41 | Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); | ||
| 42 | } // Anonymous namespace | ||
| 43 | |||
| 44 | static int SDLEventWatcher(void* user_data, SDL_Event* event) { | ||
| 45 | auto* const sdl_state = static_cast<SDLState*>(user_data); | ||
| 46 | |||
| 47 | // Don't handle the event if we are configuring | ||
| 48 | if (sdl_state->polling) { | ||
| 49 | sdl_state->event_queue.Push(*event); | ||
| 50 | } else { | ||
| 51 | sdl_state->HandleGameControllerEvent(*event); | ||
| 52 | } | ||
| 53 | |||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | |||
| 57 | class SDLJoystick { | ||
| 58 | public: | ||
| 59 | SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, | ||
| 60 | SDL_GameController* game_controller) | ||
| 61 | : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, | ||
| 62 | sdl_controller{game_controller, &SDL_GameControllerClose} { | ||
| 63 | EnableMotion(); | ||
| 64 | } | ||
| 65 | |||
| 66 | void EnableMotion() { | ||
| 67 | if (sdl_controller) { | ||
| 68 | SDL_GameController* controller = sdl_controller.get(); | ||
| 69 | if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) { | ||
| 70 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); | ||
| 71 | has_accel = true; | ||
| 72 | } | ||
| 73 | if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) { | ||
| 74 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); | ||
| 75 | has_gyro = true; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | void SetButton(int button, bool value) { | ||
| 81 | std::lock_guard lock{mutex}; | ||
| 82 | state.buttons.insert_or_assign(button, value); | ||
| 83 | } | ||
| 84 | |||
| 85 | void PreSetButton(int button) { | ||
| 86 | if (!state.buttons.contains(button)) { | ||
| 87 | SetButton(button, false); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | void SetMotion(SDL_ControllerSensorEvent event) { | ||
| 92 | constexpr float gravity_constant = 9.80665f; | ||
| 93 | std::lock_guard lock{mutex}; | ||
| 94 | u64 time_difference = event.timestamp - last_motion_update; | ||
| 95 | last_motion_update = event.timestamp; | ||
| 96 | switch (event.sensor) { | ||
| 97 | case SDL_SENSOR_ACCEL: { | ||
| 98 | const Common::Vec3f acceleration = {-event.data[0], event.data[2], -event.data[1]}; | ||
| 99 | motion.SetAcceleration(acceleration / gravity_constant); | ||
| 100 | break; | ||
| 101 | } | ||
| 102 | case SDL_SENSOR_GYRO: { | ||
| 103 | const Common::Vec3f gyroscope = {event.data[0], -event.data[2], event.data[1]}; | ||
| 104 | motion.SetGyroscope(gyroscope / (Common::PI * 2)); | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | // Ignore duplicated timestamps | ||
| 110 | if (time_difference == 0) { | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | |||
| 114 | motion.SetGyroThreshold(0.0001f); | ||
| 115 | motion.UpdateRotation(time_difference * 1000); | ||
| 116 | motion.UpdateOrientation(time_difference * 1000); | ||
| 117 | } | ||
| 118 | |||
| 119 | bool GetButton(int button) const { | ||
| 120 | std::lock_guard lock{mutex}; | ||
| 121 | return state.buttons.at(button); | ||
| 122 | } | ||
| 123 | |||
| 124 | bool ToggleButton(int button) { | ||
| 125 | std::lock_guard lock{mutex}; | ||
| 126 | |||
| 127 | if (!state.toggle_buttons.contains(button) || !state.lock_buttons.contains(button)) { | ||
| 128 | state.toggle_buttons.insert_or_assign(button, false); | ||
| 129 | state.lock_buttons.insert_or_assign(button, false); | ||
| 130 | } | ||
| 131 | |||
| 132 | const bool button_state = state.toggle_buttons.at(button); | ||
| 133 | const bool button_lock = state.lock_buttons.at(button); | ||
| 134 | |||
| 135 | if (button_lock) { | ||
| 136 | return button_state; | ||
| 137 | } | ||
| 138 | |||
| 139 | state.lock_buttons.insert_or_assign(button, true); | ||
| 140 | |||
| 141 | if (button_state) { | ||
| 142 | state.toggle_buttons.insert_or_assign(button, false); | ||
| 143 | } else { | ||
| 144 | state.toggle_buttons.insert_or_assign(button, true); | ||
| 145 | } | ||
| 146 | |||
| 147 | return !button_state; | ||
| 148 | } | ||
| 149 | |||
| 150 | bool UnlockButton(int button) { | ||
| 151 | std::lock_guard lock{mutex}; | ||
| 152 | if (!state.toggle_buttons.contains(button)) { | ||
| 153 | return false; | ||
| 154 | } | ||
| 155 | state.lock_buttons.insert_or_assign(button, false); | ||
| 156 | return state.toggle_buttons.at(button); | ||
| 157 | } | ||
| 158 | |||
| 159 | void SetAxis(int axis, Sint16 value) { | ||
| 160 | std::lock_guard lock{mutex}; | ||
| 161 | state.axes.insert_or_assign(axis, value); | ||
| 162 | } | ||
| 163 | |||
| 164 | void PreSetAxis(int axis) { | ||
| 165 | if (!state.axes.contains(axis)) { | ||
| 166 | SetAxis(axis, 0); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | float GetAxis(int axis, float range, float offset) const { | ||
| 171 | std::lock_guard lock{mutex}; | ||
| 172 | const float value = static_cast<float>(state.axes.at(axis)) / 32767.0f; | ||
| 173 | const float offset_scale = (value + offset) > 0.0f ? 1.0f + offset : 1.0f - offset; | ||
| 174 | return (value + offset) / range / offset_scale; | ||
| 175 | } | ||
| 176 | |||
| 177 | bool RumblePlay(u16 amp_low, u16 amp_high) { | ||
| 178 | constexpr u32 rumble_max_duration_ms = 1000; | ||
| 179 | |||
| 180 | if (sdl_controller) { | ||
| 181 | return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, | ||
| 182 | rumble_max_duration_ms) != -1; | ||
| 183 | } else if (sdl_joystick) { | ||
| 184 | return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, | ||
| 185 | rumble_max_duration_ms) != -1; | ||
| 186 | } | ||
| 187 | |||
| 188 | return false; | ||
| 189 | } | ||
| 190 | |||
| 191 | std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range, float offset_x, | ||
| 192 | float offset_y) const { | ||
| 193 | float x = GetAxis(axis_x, range, offset_x); | ||
| 194 | float y = GetAxis(axis_y, range, offset_y); | ||
| 195 | y = -y; // 3DS uses an y-axis inverse from SDL | ||
| 196 | |||
| 197 | // Make sure the coordinates are in the unit circle, | ||
| 198 | // otherwise normalize it. | ||
| 199 | float r = x * x + y * y; | ||
| 200 | if (r > 1.0f) { | ||
| 201 | r = std::sqrt(r); | ||
| 202 | x /= r; | ||
| 203 | y /= r; | ||
| 204 | } | ||
| 205 | |||
| 206 | return std::make_tuple(x, y); | ||
| 207 | } | ||
| 208 | |||
| 209 | bool HasGyro() const { | ||
| 210 | return has_gyro; | ||
| 211 | } | ||
| 212 | |||
| 213 | bool HasAccel() const { | ||
| 214 | return has_accel; | ||
| 215 | } | ||
| 216 | |||
| 217 | const MotionInput& GetMotion() const { | ||
| 218 | return motion; | ||
| 219 | } | ||
| 220 | |||
| 221 | void SetHat(int hat, Uint8 direction) { | ||
| 222 | std::lock_guard lock{mutex}; | ||
| 223 | state.hats.insert_or_assign(hat, direction); | ||
| 224 | } | ||
| 225 | |||
| 226 | bool GetHatDirection(int hat, Uint8 direction) const { | ||
| 227 | std::lock_guard lock{mutex}; | ||
| 228 | return (state.hats.at(hat) & direction) != 0; | ||
| 229 | } | ||
| 230 | /** | ||
| 231 | * The guid of the joystick | ||
| 232 | */ | ||
| 233 | const std::string& GetGUID() const { | ||
| 234 | return guid; | ||
| 235 | } | ||
| 236 | |||
| 237 | /** | ||
| 238 | * The number of joystick from the same type that were connected before this joystick | ||
| 239 | */ | ||
| 240 | int GetPort() const { | ||
| 241 | return port; | ||
| 242 | } | ||
| 243 | |||
| 244 | SDL_Joystick* GetSDLJoystick() const { | ||
| 245 | return sdl_joystick.get(); | ||
| 246 | } | ||
| 247 | |||
| 248 | SDL_GameController* GetSDLGameController() const { | ||
| 249 | return sdl_controller.get(); | ||
| 250 | } | ||
| 251 | |||
| 252 | void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) { | ||
| 253 | sdl_joystick.reset(joystick); | ||
| 254 | sdl_controller.reset(controller); | ||
| 255 | } | ||
| 256 | |||
| 257 | bool IsJoyconLeft() const { | ||
| 258 | const std::string controller_name = GetControllerName(); | ||
| 259 | if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) { | ||
| 260 | return true; | ||
| 261 | } | ||
| 262 | if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) { | ||
| 263 | return true; | ||
| 264 | } | ||
| 265 | return false; | ||
| 266 | } | ||
| 267 | |||
| 268 | bool IsJoyconRight() const { | ||
| 269 | const std::string controller_name = GetControllerName(); | ||
| 270 | if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) { | ||
| 271 | return true; | ||
| 272 | } | ||
| 273 | if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) { | ||
| 274 | return true; | ||
| 275 | } | ||
| 276 | return false; | ||
| 277 | } | ||
| 278 | |||
| 279 | std::string GetControllerName() const { | ||
| 280 | if (sdl_controller) { | ||
| 281 | switch (SDL_GameControllerGetType(sdl_controller.get())) { | ||
| 282 | case SDL_CONTROLLER_TYPE_XBOX360: | ||
| 283 | return "XBox 360 Controller"; | ||
| 284 | case SDL_CONTROLLER_TYPE_XBOXONE: | ||
| 285 | return "XBox One Controller"; | ||
| 286 | default: | ||
| 287 | break; | ||
| 288 | } | ||
| 289 | const auto name = SDL_GameControllerName(sdl_controller.get()); | ||
| 290 | if (name) { | ||
| 291 | return name; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | if (sdl_joystick) { | ||
| 296 | const auto name = SDL_JoystickName(sdl_joystick.get()); | ||
| 297 | if (name) { | ||
| 298 | return name; | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | return "Unknown"; | ||
| 303 | } | ||
| 304 | |||
| 305 | private: | ||
| 306 | struct State { | ||
| 307 | std::unordered_map<int, bool> buttons; | ||
| 308 | std::unordered_map<int, bool> toggle_buttons{}; | ||
| 309 | std::unordered_map<int, bool> lock_buttons{}; | ||
| 310 | std::unordered_map<int, Sint16> axes; | ||
| 311 | std::unordered_map<int, Uint8> hats; | ||
| 312 | } state; | ||
| 313 | std::string guid; | ||
| 314 | int port; | ||
| 315 | std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; | ||
| 316 | std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; | ||
| 317 | mutable std::mutex mutex; | ||
| 318 | |||
| 319 | // Motion is initialized with the PID values | ||
| 320 | MotionInput motion{0.3f, 0.005f, 0.0f}; | ||
| 321 | u64 last_motion_update{}; | ||
| 322 | bool has_gyro{false}; | ||
| 323 | bool has_accel{false}; | ||
| 324 | }; | ||
| 325 | |||
| 326 | std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { | ||
| 327 | std::lock_guard lock{joystick_map_mutex}; | ||
| 328 | const auto it = joystick_map.find(guid); | ||
| 329 | |||
| 330 | if (it != joystick_map.end()) { | ||
| 331 | while (it->second.size() <= static_cast<std::size_t>(port)) { | ||
| 332 | auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), | ||
| 333 | nullptr, nullptr); | ||
| 334 | it->second.emplace_back(std::move(joystick)); | ||
| 335 | } | ||
| 336 | |||
| 337 | return it->second[static_cast<std::size_t>(port)]; | ||
| 338 | } | ||
| 339 | |||
| 340 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr); | ||
| 341 | |||
| 342 | return joystick_map[guid].emplace_back(std::move(joystick)); | ||
| 343 | } | ||
| 344 | |||
| 345 | std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { | ||
| 346 | auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); | ||
| 347 | const std::string guid = GetGUID(sdl_joystick); | ||
| 348 | |||
| 349 | std::lock_guard lock{joystick_map_mutex}; | ||
| 350 | const auto map_it = joystick_map.find(guid); | ||
| 351 | |||
| 352 | if (map_it == joystick_map.end()) { | ||
| 353 | return nullptr; | ||
| 354 | } | ||
| 355 | |||
| 356 | const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), | ||
| 357 | [&sdl_joystick](const auto& joystick) { | ||
| 358 | return joystick->GetSDLJoystick() == sdl_joystick; | ||
| 359 | }); | ||
| 360 | |||
| 361 | if (vec_it == map_it->second.end()) { | ||
| 362 | return nullptr; | ||
| 363 | } | ||
| 364 | |||
| 365 | return *vec_it; | ||
| 366 | } | ||
| 367 | |||
| 368 | void SDLState::InitJoystick(int joystick_index) { | ||
| 369 | SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); | ||
| 370 | SDL_GameController* sdl_gamecontroller = nullptr; | ||
| 371 | |||
| 372 | if (SDL_IsGameController(joystick_index)) { | ||
| 373 | sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); | ||
| 374 | } | ||
| 375 | |||
| 376 | if (!sdl_joystick) { | ||
| 377 | LOG_ERROR(Input, "Failed to open joystick {}", joystick_index); | ||
| 378 | return; | ||
| 379 | } | ||
| 380 | |||
| 381 | const std::string guid = GetGUID(sdl_joystick); | ||
| 382 | |||
| 383 | std::lock_guard lock{joystick_map_mutex}; | ||
| 384 | if (joystick_map.find(guid) == joystick_map.end()) { | ||
| 385 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); | ||
| 386 | joystick_map[guid].emplace_back(std::move(joystick)); | ||
| 387 | return; | ||
| 388 | } | ||
| 389 | |||
| 390 | auto& joystick_guid_list = joystick_map[guid]; | ||
| 391 | const auto joystick_it = | ||
| 392 | std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), | ||
| 393 | [](const auto& joystick) { return !joystick->GetSDLJoystick(); }); | ||
| 394 | |||
| 395 | if (joystick_it != joystick_guid_list.end()) { | ||
| 396 | (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); | ||
| 397 | return; | ||
| 398 | } | ||
| 399 | |||
| 400 | const int port = static_cast<int>(joystick_guid_list.size()); | ||
| 401 | auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); | ||
| 402 | joystick_guid_list.emplace_back(std::move(joystick)); | ||
| 403 | } | ||
| 404 | |||
| 405 | void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { | ||
| 406 | const std::string guid = GetGUID(sdl_joystick); | ||
| 407 | |||
| 408 | std::lock_guard lock{joystick_map_mutex}; | ||
| 409 | // This call to guid is safe since the joystick is guaranteed to be in the map | ||
| 410 | const auto& joystick_guid_list = joystick_map[guid]; | ||
| 411 | const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), | ||
| 412 | [&sdl_joystick](const auto& joystick) { | ||
| 413 | return joystick->GetSDLJoystick() == sdl_joystick; | ||
| 414 | }); | ||
| 415 | |||
| 416 | if (joystick_it != joystick_guid_list.end()) { | ||
| 417 | (*joystick_it)->SetSDLJoystick(nullptr, nullptr); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | void SDLState::HandleGameControllerEvent(const SDL_Event& event) { | ||
| 422 | switch (event.type) { | ||
| 423 | case SDL_JOYBUTTONUP: { | ||
| 424 | if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { | ||
| 425 | joystick->SetButton(event.jbutton.button, false); | ||
| 426 | } | ||
| 427 | break; | ||
| 428 | } | ||
| 429 | case SDL_JOYBUTTONDOWN: { | ||
| 430 | if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { | ||
| 431 | joystick->SetButton(event.jbutton.button, true); | ||
| 432 | } | ||
| 433 | break; | ||
| 434 | } | ||
| 435 | case SDL_JOYHATMOTION: { | ||
| 436 | if (auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) { | ||
| 437 | joystick->SetHat(event.jhat.hat, event.jhat.value); | ||
| 438 | } | ||
| 439 | break; | ||
| 440 | } | ||
| 441 | case SDL_JOYAXISMOTION: { | ||
| 442 | if (auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) { | ||
| 443 | joystick->SetAxis(event.jaxis.axis, event.jaxis.value); | ||
| 444 | } | ||
| 445 | break; | ||
| 446 | } | ||
| 447 | case SDL_CONTROLLERSENSORUPDATE: { | ||
| 448 | if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) { | ||
| 449 | joystick->SetMotion(event.csensor); | ||
| 450 | } | ||
| 451 | break; | ||
| 452 | } | ||
| 453 | case SDL_JOYDEVICEREMOVED: | ||
| 454 | LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); | ||
| 455 | CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); | ||
| 456 | break; | ||
| 457 | case SDL_JOYDEVICEADDED: | ||
| 458 | LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which); | ||
| 459 | InitJoystick(event.jdevice.which); | ||
| 460 | break; | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | void SDLState::CloseJoysticks() { | ||
| 465 | std::lock_guard lock{joystick_map_mutex}; | ||
| 466 | joystick_map.clear(); | ||
| 467 | } | ||
| 468 | |||
| 469 | class SDLButton final : public Input::ButtonDevice { | ||
| 470 | public: | ||
| 471 | explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_, bool toggle_) | ||
| 472 | : joystick(std::move(joystick_)), button(button_), toggle(toggle_) {} | ||
| 473 | |||
| 474 | bool GetStatus() const override { | ||
| 475 | const bool button_state = joystick->GetButton(button); | ||
| 476 | if (!toggle) { | ||
| 477 | return button_state; | ||
| 478 | } | ||
| 479 | |||
| 480 | if (button_state) { | ||
| 481 | return joystick->ToggleButton(button); | ||
| 482 | } | ||
| 483 | return joystick->UnlockButton(button); | ||
| 484 | } | ||
| 485 | |||
| 486 | private: | ||
| 487 | std::shared_ptr<SDLJoystick> joystick; | ||
| 488 | int button; | ||
| 489 | bool toggle; | ||
| 490 | }; | ||
| 491 | |||
| 492 | class SDLDirectionButton final : public Input::ButtonDevice { | ||
| 493 | public: | ||
| 494 | explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) | ||
| 495 | : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {} | ||
| 496 | |||
| 497 | bool GetStatus() const override { | ||
| 498 | return joystick->GetHatDirection(hat, direction); | ||
| 499 | } | ||
| 500 | |||
| 501 | private: | ||
| 502 | std::shared_ptr<SDLJoystick> joystick; | ||
| 503 | int hat; | ||
| 504 | Uint8 direction; | ||
| 505 | }; | ||
| 506 | |||
| 507 | class SDLAxisButton final : public Input::ButtonDevice { | ||
| 508 | public: | ||
| 509 | explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_, | ||
| 510 | bool trigger_if_greater_) | ||
| 511 | : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_), | ||
| 512 | trigger_if_greater(trigger_if_greater_) {} | ||
| 513 | |||
| 514 | bool GetStatus() const override { | ||
| 515 | const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f); | ||
| 516 | if (trigger_if_greater) { | ||
| 517 | return axis_value > threshold; | ||
| 518 | } | ||
| 519 | return axis_value < threshold; | ||
| 520 | } | ||
| 521 | |||
| 522 | private: | ||
| 523 | std::shared_ptr<SDLJoystick> joystick; | ||
| 524 | int axis; | ||
| 525 | float threshold; | ||
| 526 | bool trigger_if_greater; | ||
| 527 | }; | ||
| 528 | |||
| 529 | class SDLAnalog final : public Input::AnalogDevice { | ||
| 530 | public: | ||
| 531 | explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, | ||
| 532 | bool invert_x_, bool invert_y_, float deadzone_, float range_, | ||
| 533 | float offset_x_, float offset_y_) | ||
| 534 | : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), | ||
| 535 | invert_y(invert_y_), deadzone(deadzone_), range(range_), offset_x(offset_x_), | ||
| 536 | offset_y(offset_y_) {} | ||
| 537 | |||
| 538 | std::tuple<float, float> GetStatus() const override { | ||
| 539 | auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range, offset_x, offset_y); | ||
| 540 | const float r = std::sqrt((x * x) + (y * y)); | ||
| 541 | if (invert_x) { | ||
| 542 | x = -x; | ||
| 543 | } | ||
| 544 | if (invert_y) { | ||
| 545 | y = -y; | ||
| 546 | } | ||
| 547 | |||
| 548 | if (r > deadzone) { | ||
| 549 | return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), | ||
| 550 | y / r * (r - deadzone) / (1 - deadzone)); | ||
| 551 | } | ||
| 552 | return {}; | ||
| 553 | } | ||
| 554 | |||
| 555 | std::tuple<float, float> GetRawStatus() const override { | ||
| 556 | const float x = joystick->GetAxis(axis_x, range, offset_x); | ||
| 557 | const float y = joystick->GetAxis(axis_y, range, offset_y); | ||
| 558 | return {x, -y}; | ||
| 559 | } | ||
| 560 | |||
| 561 | Input::AnalogProperties GetAnalogProperties() const override { | ||
| 562 | return {deadzone, range, 0.5f}; | ||
| 563 | } | ||
| 564 | |||
| 565 | bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { | ||
| 566 | const auto [x, y] = GetStatus(); | ||
| 567 | const float directional_deadzone = 0.5f; | ||
| 568 | switch (direction) { | ||
| 569 | case Input::AnalogDirection::RIGHT: | ||
| 570 | return x > directional_deadzone; | ||
| 571 | case Input::AnalogDirection::LEFT: | ||
| 572 | return x < -directional_deadzone; | ||
| 573 | case Input::AnalogDirection::UP: | ||
| 574 | return y > directional_deadzone; | ||
| 575 | case Input::AnalogDirection::DOWN: | ||
| 576 | return y < -directional_deadzone; | ||
| 577 | } | ||
| 578 | return false; | ||
| 579 | } | ||
| 580 | |||
| 581 | private: | ||
| 582 | std::shared_ptr<SDLJoystick> joystick; | ||
| 583 | const int axis_x; | ||
| 584 | const int axis_y; | ||
| 585 | const bool invert_x; | ||
| 586 | const bool invert_y; | ||
| 587 | const float deadzone; | ||
| 588 | const float range; | ||
| 589 | const float offset_x; | ||
| 590 | const float offset_y; | ||
| 591 | }; | ||
| 592 | |||
| 593 | class SDLVibration final : public Input::VibrationDevice { | ||
| 594 | public: | ||
| 595 | explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_) | ||
| 596 | : joystick(std::move(joystick_)) {} | ||
| 597 | |||
| 598 | u8 GetStatus() const override { | ||
| 599 | joystick->RumblePlay(1, 1); | ||
| 600 | return joystick->RumblePlay(0, 0); | ||
| 601 | } | ||
| 602 | |||
| 603 | bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high, | ||
| 604 | [[maybe_unused]] f32 freq_high) const override { | ||
| 605 | const auto process_amplitude = [](f32 amplitude) { | ||
| 606 | return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF); | ||
| 607 | }; | ||
| 608 | |||
| 609 | const auto processed_amp_low = process_amplitude(amp_low); | ||
| 610 | const auto processed_amp_high = process_amplitude(amp_high); | ||
| 611 | |||
| 612 | return joystick->RumblePlay(processed_amp_low, processed_amp_high); | ||
| 613 | } | ||
| 614 | |||
| 615 | private: | ||
| 616 | std::shared_ptr<SDLJoystick> joystick; | ||
| 617 | }; | ||
| 618 | |||
| 619 | class SDLMotion final : public Input::MotionDevice { | ||
| 620 | public: | ||
| 621 | explicit SDLMotion(std::shared_ptr<SDLJoystick> joystick_) : joystick(std::move(joystick_)) {} | ||
| 622 | |||
| 623 | Input::MotionStatus GetStatus() const override { | ||
| 624 | return joystick->GetMotion().GetMotion(); | ||
| 625 | } | ||
| 626 | |||
| 627 | private: | ||
| 628 | std::shared_ptr<SDLJoystick> joystick; | ||
| 629 | }; | ||
| 630 | |||
| 631 | class SDLDirectionMotion final : public Input::MotionDevice { | ||
| 632 | public: | ||
| 633 | explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) | ||
| 634 | : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {} | ||
| 635 | |||
| 636 | Input::MotionStatus GetStatus() const override { | ||
| 637 | if (joystick->GetHatDirection(hat, direction)) { | ||
| 638 | return joystick->GetMotion().GetRandomMotion(2, 6); | ||
| 639 | } | ||
| 640 | return joystick->GetMotion().GetRandomMotion(0, 0); | ||
| 641 | } | ||
| 642 | |||
| 643 | private: | ||
| 644 | std::shared_ptr<SDLJoystick> joystick; | ||
| 645 | int hat; | ||
| 646 | Uint8 direction; | ||
| 647 | }; | ||
| 648 | |||
| 649 | class SDLAxisMotion final : public Input::MotionDevice { | ||
| 650 | public: | ||
| 651 | explicit SDLAxisMotion(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_, | ||
| 652 | bool trigger_if_greater_) | ||
| 653 | : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_), | ||
| 654 | trigger_if_greater(trigger_if_greater_) {} | ||
| 655 | |||
| 656 | Input::MotionStatus GetStatus() const override { | ||
| 657 | const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f); | ||
| 658 | bool trigger = axis_value < threshold; | ||
| 659 | if (trigger_if_greater) { | ||
| 660 | trigger = axis_value > threshold; | ||
| 661 | } | ||
| 662 | |||
| 663 | if (trigger) { | ||
| 664 | return joystick->GetMotion().GetRandomMotion(2, 6); | ||
| 665 | } | ||
| 666 | return joystick->GetMotion().GetRandomMotion(0, 0); | ||
| 667 | } | ||
| 668 | |||
| 669 | private: | ||
| 670 | std::shared_ptr<SDLJoystick> joystick; | ||
| 671 | int axis; | ||
| 672 | float threshold; | ||
| 673 | bool trigger_if_greater; | ||
| 674 | }; | ||
| 675 | |||
| 676 | class SDLButtonMotion final : public Input::MotionDevice { | ||
| 677 | public: | ||
| 678 | explicit SDLButtonMotion(std::shared_ptr<SDLJoystick> joystick_, int button_) | ||
| 679 | : joystick(std::move(joystick_)), button(button_) {} | ||
| 680 | |||
| 681 | Input::MotionStatus GetStatus() const override { | ||
| 682 | if (joystick->GetButton(button)) { | ||
| 683 | return joystick->GetMotion().GetRandomMotion(2, 6); | ||
| 684 | } | ||
| 685 | return joystick->GetMotion().GetRandomMotion(0, 0); | ||
| 686 | } | ||
| 687 | |||
| 688 | private: | ||
| 689 | std::shared_ptr<SDLJoystick> joystick; | ||
| 690 | int button; | ||
| 691 | }; | ||
| 692 | |||
| 693 | /// A button device factory that creates button devices from SDL joystick | ||
| 694 | class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { | ||
| 695 | public: | ||
| 696 | explicit SDLButtonFactory(SDLState& state_) : state(state_) {} | ||
| 697 | |||
| 698 | /** | ||
| 699 | * Creates a button device from a joystick button | ||
| 700 | * @param params contains parameters for creating the device: | ||
| 701 | * - "guid": the guid of the joystick to bind | ||
| 702 | * - "port": the nth joystick of the same type to bind | ||
| 703 | * - "button"(optional): the index of the button to bind | ||
| 704 | * - "hat"(optional): the index of the hat to bind as direction buttons | ||
| 705 | * - "axis"(optional): the index of the axis to bind | ||
| 706 | * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", | ||
| 707 | * "down", "left" or "right" | ||
| 708 | * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is | ||
| 709 | * triggered if the axis value crosses | ||
| 710 | * - "direction"(only used for axis): "+" means the button is triggered when the axis | ||
| 711 | * value is greater than the threshold; "-" means the button is triggered when the axis | ||
| 712 | * value is smaller than the threshold | ||
| 713 | */ | ||
| 714 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { | ||
| 715 | const std::string guid = params.Get("guid", "0"); | ||
| 716 | const int port = params.Get("port", 0); | ||
| 717 | const auto toggle = params.Get("toggle", false); | ||
| 718 | |||
| 719 | auto joystick = state.GetSDLJoystickByGUID(guid, port); | ||
| 720 | |||
| 721 | if (params.Has("hat")) { | ||
| 722 | const int hat = params.Get("hat", 0); | ||
| 723 | const std::string direction_name = params.Get("direction", ""); | ||
| 724 | Uint8 direction; | ||
| 725 | if (direction_name == "up") { | ||
| 726 | direction = SDL_HAT_UP; | ||
| 727 | } else if (direction_name == "down") { | ||
| 728 | direction = SDL_HAT_DOWN; | ||
| 729 | } else if (direction_name == "left") { | ||
| 730 | direction = SDL_HAT_LEFT; | ||
| 731 | } else if (direction_name == "right") { | ||
| 732 | direction = SDL_HAT_RIGHT; | ||
| 733 | } else { | ||
| 734 | direction = 0; | ||
| 735 | } | ||
| 736 | // This is necessary so accessing GetHat with hat won't crash | ||
| 737 | joystick->SetHat(hat, SDL_HAT_CENTERED); | ||
| 738 | return std::make_unique<SDLDirectionButton>(joystick, hat, direction); | ||
| 739 | } | ||
| 740 | |||
| 741 | if (params.Has("axis")) { | ||
| 742 | const int axis = params.Get("axis", 0); | ||
| 743 | // Convert range from (0.0, 1.0) to (-1.0, 1.0) | ||
| 744 | const float threshold = (params.Get("threshold", 0.5f) - 0.5f) * 2.0f; | ||
| 745 | const std::string direction_name = params.Get("direction", ""); | ||
| 746 | bool trigger_if_greater; | ||
| 747 | if (direction_name == "+") { | ||
| 748 | trigger_if_greater = true; | ||
| 749 | } else if (direction_name == "-") { | ||
| 750 | trigger_if_greater = false; | ||
| 751 | } else { | ||
| 752 | trigger_if_greater = true; | ||
| 753 | LOG_ERROR(Input, "Unknown direction {}", direction_name); | ||
| 754 | } | ||
| 755 | // This is necessary so accessing GetAxis with axis won't crash | ||
| 756 | joystick->PreSetAxis(axis); | ||
| 757 | return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater); | ||
| 758 | } | ||
| 759 | |||
| 760 | const int button = params.Get("button", 0); | ||
| 761 | // This is necessary so accessing GetButton with button won't crash | ||
| 762 | joystick->PreSetButton(button); | ||
| 763 | return std::make_unique<SDLButton>(joystick, button, toggle); | ||
| 764 | } | ||
| 765 | |||
| 766 | private: | ||
| 767 | SDLState& state; | ||
| 768 | }; | ||
| 769 | |||
| 770 | /// An analog device factory that creates analog devices from SDL joystick | ||
| 771 | class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> { | ||
| 772 | public: | ||
| 773 | explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} | ||
| 774 | /** | ||
| 775 | * Creates an analog device from joystick axes | ||
| 776 | * @param params contains parameters for creating the device: | ||
| 777 | * - "guid": the guid of the joystick to bind | ||
| 778 | * - "port": the nth joystick of the same type | ||
| 779 | * - "axis_x": the index of the axis to be bind as x-axis | ||
| 780 | * - "axis_y": the index of the axis to be bind as y-axis | ||
| 781 | */ | ||
| 782 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override { | ||
| 783 | const std::string guid = params.Get("guid", "0"); | ||
| 784 | const int port = params.Get("port", 0); | ||
| 785 | const int axis_x = params.Get("axis_x", 0); | ||
| 786 | const int axis_y = params.Get("axis_y", 1); | ||
| 787 | const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); | ||
| 788 | const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); | ||
| 789 | const std::string invert_x_value = params.Get("invert_x", "+"); | ||
| 790 | const std::string invert_y_value = params.Get("invert_y", "+"); | ||
| 791 | const bool invert_x = invert_x_value == "-"; | ||
| 792 | const bool invert_y = invert_y_value == "-"; | ||
| 793 | const float offset_x = std::clamp(params.Get("offset_x", 0.0f), -0.99f, 0.99f); | ||
| 794 | const float offset_y = std::clamp(params.Get("offset_y", 0.0f), -0.99f, 0.99f); | ||
| 795 | auto joystick = state.GetSDLJoystickByGUID(guid, port); | ||
| 796 | |||
| 797 | // This is necessary so accessing GetAxis with axis_x and axis_y won't crash | ||
| 798 | joystick->PreSetAxis(axis_x); | ||
| 799 | joystick->PreSetAxis(axis_y); | ||
| 800 | return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone, | ||
| 801 | range, offset_x, offset_y); | ||
| 802 | } | ||
| 803 | |||
| 804 | private: | ||
| 805 | SDLState& state; | ||
| 806 | }; | ||
| 807 | |||
| 808 | /// An vibration device factory that creates vibration devices from SDL joystick | ||
| 809 | class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> { | ||
| 810 | public: | ||
| 811 | explicit SDLVibrationFactory(SDLState& state_) : state(state_) {} | ||
| 812 | /** | ||
| 813 | * Creates a vibration device from a joystick | ||
| 814 | * @param params contains parameters for creating the device: | ||
| 815 | * - "guid": the guid of the joystick to bind | ||
| 816 | * - "port": the nth joystick of the same type | ||
| 817 | */ | ||
| 818 | std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override { | ||
| 819 | const std::string guid = params.Get("guid", "0"); | ||
| 820 | const int port = params.Get("port", 0); | ||
| 821 | return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port)); | ||
| 822 | } | ||
| 823 | |||
| 824 | private: | ||
| 825 | SDLState& state; | ||
| 826 | }; | ||
| 827 | |||
| 828 | /// A motion device factory that creates motion devices from SDL joystick | ||
| 829 | class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> { | ||
| 830 | public: | ||
| 831 | explicit SDLMotionFactory(SDLState& state_) : state(state_) {} | ||
| 832 | /** | ||
| 833 | * Creates motion device from joystick axes | ||
| 834 | * @param params contains parameters for creating the device: | ||
| 835 | * - "guid": the guid of the joystick to bind | ||
| 836 | * - "port": the nth joystick of the same type | ||
| 837 | */ | ||
| 838 | std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override { | ||
| 839 | const std::string guid = params.Get("guid", "0"); | ||
| 840 | const int port = params.Get("port", 0); | ||
| 841 | |||
| 842 | auto joystick = state.GetSDLJoystickByGUID(guid, port); | ||
| 843 | |||
| 844 | if (params.Has("motion")) { | ||
| 845 | return std::make_unique<SDLMotion>(joystick); | ||
| 846 | } | ||
| 847 | |||
| 848 | if (params.Has("hat")) { | ||
| 849 | const int hat = params.Get("hat", 0); | ||
| 850 | const std::string direction_name = params.Get("direction", ""); | ||
| 851 | Uint8 direction; | ||
| 852 | if (direction_name == "up") { | ||
| 853 | direction = SDL_HAT_UP; | ||
| 854 | } else if (direction_name == "down") { | ||
| 855 | direction = SDL_HAT_DOWN; | ||
| 856 | } else if (direction_name == "left") { | ||
| 857 | direction = SDL_HAT_LEFT; | ||
| 858 | } else if (direction_name == "right") { | ||
| 859 | direction = SDL_HAT_RIGHT; | ||
| 860 | } else { | ||
| 861 | direction = 0; | ||
| 862 | } | ||
| 863 | // This is necessary so accessing GetHat with hat won't crash | ||
| 864 | joystick->SetHat(hat, SDL_HAT_CENTERED); | ||
| 865 | return std::make_unique<SDLDirectionMotion>(joystick, hat, direction); | ||
| 866 | } | ||
| 867 | |||
| 868 | if (params.Has("axis")) { | ||
| 869 | const int axis = params.Get("axis", 0); | ||
| 870 | const float threshold = params.Get("threshold", 0.5f); | ||
| 871 | const std::string direction_name = params.Get("direction", ""); | ||
| 872 | bool trigger_if_greater; | ||
| 873 | if (direction_name == "+") { | ||
| 874 | trigger_if_greater = true; | ||
| 875 | } else if (direction_name == "-") { | ||
| 876 | trigger_if_greater = false; | ||
| 877 | } else { | ||
| 878 | trigger_if_greater = true; | ||
| 879 | LOG_ERROR(Input, "Unknown direction {}", direction_name); | ||
| 880 | } | ||
| 881 | // This is necessary so accessing GetAxis with axis won't crash | ||
| 882 | joystick->PreSetAxis(axis); | ||
| 883 | return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater); | ||
| 884 | } | ||
| 885 | |||
| 886 | const int button = params.Get("button", 0); | ||
| 887 | // This is necessary so accessing GetButton with button won't crash | ||
| 888 | joystick->PreSetButton(button); | ||
| 889 | return std::make_unique<SDLButtonMotion>(joystick, button); | ||
| 890 | } | ||
| 891 | |||
| 892 | private: | ||
| 893 | SDLState& state; | ||
| 894 | }; | ||
| 895 | |||
| 896 | SDLState::SDLState() { | ||
| 897 | using namespace Input; | ||
| 898 | button_factory = std::make_shared<SDLButtonFactory>(*this); | ||
| 899 | analog_factory = std::make_shared<SDLAnalogFactory>(*this); | ||
| 900 | vibration_factory = std::make_shared<SDLVibrationFactory>(*this); | ||
| 901 | motion_factory = std::make_shared<SDLMotionFactory>(*this); | ||
| 902 | RegisterFactory<ButtonDevice>("sdl", button_factory); | ||
| 903 | RegisterFactory<AnalogDevice>("sdl", analog_factory); | ||
| 904 | RegisterFactory<VibrationDevice>("sdl", vibration_factory); | ||
| 905 | RegisterFactory<MotionDevice>("sdl", motion_factory); | ||
| 906 | |||
| 907 | if (!Settings::values.enable_raw_input) { | ||
| 908 | // Disable raw input. When enabled this setting causes SDL to die when a web applet opens | ||
| 909 | SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0"); | ||
| 910 | } | ||
| 911 | |||
| 912 | // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers | ||
| 913 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); | ||
| 914 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); | ||
| 915 | |||
| 916 | // Tell SDL2 to use the hidapi driver. This will allow joycons to be detected as a | ||
| 917 | // GameController and not a generic one | ||
| 918 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); | ||
| 919 | |||
| 920 | // Turn off Pro controller home led | ||
| 921 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); | ||
| 922 | |||
| 923 | // If the frontend is going to manage the event loop, then we don't start one here | ||
| 924 | start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0; | ||
| 925 | if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) { | ||
| 926 | LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); | ||
| 927 | return; | ||
| 928 | } | ||
| 929 | has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0; | ||
| 930 | if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { | ||
| 931 | LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); | ||
| 932 | } | ||
| 933 | |||
| 934 | SDL_AddEventWatch(&SDLEventWatcher, this); | ||
| 935 | |||
| 936 | initialized = true; | ||
| 937 | if (start_thread) { | ||
| 938 | poll_thread = std::thread([this] { | ||
| 939 | using namespace std::chrono_literals; | ||
| 940 | while (initialized) { | ||
| 941 | SDL_PumpEvents(); | ||
| 942 | std::this_thread::sleep_for(1ms); | ||
| 943 | } | ||
| 944 | }); | ||
| 945 | } | ||
| 946 | // Because the events for joystick connection happens before we have our event watcher added, we | ||
| 947 | // can just open all the joysticks right here | ||
| 948 | for (int i = 0; i < SDL_NumJoysticks(); ++i) { | ||
| 949 | InitJoystick(i); | ||
| 950 | } | ||
| 951 | } | ||
| 952 | |||
| 953 | SDLState::~SDLState() { | ||
| 954 | using namespace Input; | ||
| 955 | UnregisterFactory<ButtonDevice>("sdl"); | ||
| 956 | UnregisterFactory<AnalogDevice>("sdl"); | ||
| 957 | UnregisterFactory<VibrationDevice>("sdl"); | ||
| 958 | UnregisterFactory<MotionDevice>("sdl"); | ||
| 959 | |||
| 960 | CloseJoysticks(); | ||
| 961 | SDL_DelEventWatch(&SDLEventWatcher, this); | ||
| 962 | |||
| 963 | initialized = false; | ||
| 964 | if (start_thread) { | ||
| 965 | poll_thread.join(); | ||
| 966 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK); | ||
| 967 | } | ||
| 968 | } | ||
| 969 | |||
| 970 | std::vector<Common::ParamPackage> SDLState::GetInputDevices() { | ||
| 971 | std::scoped_lock lock(joystick_map_mutex); | ||
| 972 | std::vector<Common::ParamPackage> devices; | ||
| 973 | std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs; | ||
| 974 | for (const auto& [key, value] : joystick_map) { | ||
| 975 | for (const auto& joystick : value) { | ||
| 976 | if (!joystick->GetSDLJoystick()) { | ||
| 977 | continue; | ||
| 978 | } | ||
| 979 | std::string name = | ||
| 980 | fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort()); | ||
| 981 | devices.emplace_back(Common::ParamPackage{ | ||
| 982 | {"class", "sdl"}, | ||
| 983 | {"display", std::move(name)}, | ||
| 984 | {"guid", joystick->GetGUID()}, | ||
| 985 | {"port", std::to_string(joystick->GetPort())}, | ||
| 986 | }); | ||
| 987 | if (joystick->IsJoyconLeft()) { | ||
| 988 | joycon_pairs.insert_or_assign(joystick->GetPort(), joystick); | ||
| 989 | } | ||
| 990 | } | ||
| 991 | } | ||
| 992 | |||
| 993 | // Add dual controllers | ||
| 994 | for (const auto& [key, value] : joystick_map) { | ||
| 995 | for (const auto& joystick : value) { | ||
| 996 | if (joystick->IsJoyconRight()) { | ||
| 997 | if (!joycon_pairs.contains(joystick->GetPort())) { | ||
| 998 | continue; | ||
| 999 | } | ||
| 1000 | const auto joystick2 = joycon_pairs.at(joystick->GetPort()); | ||
| 1001 | |||
| 1002 | std::string name = | ||
| 1003 | fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort()); | ||
| 1004 | devices.emplace_back(Common::ParamPackage{ | ||
| 1005 | {"class", "sdl"}, | ||
| 1006 | {"display", std::move(name)}, | ||
| 1007 | {"guid", joystick->GetGUID()}, | ||
| 1008 | {"guid2", joystick2->GetGUID()}, | ||
| 1009 | {"port", std::to_string(joystick->GetPort())}, | ||
| 1010 | }); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | return devices; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | namespace { | ||
| 1018 | Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, | ||
| 1019 | float value = 0.1f) { | ||
| 1020 | Common::ParamPackage params({{"engine", "sdl"}}); | ||
| 1021 | params.Set("port", port); | ||
| 1022 | params.Set("guid", std::move(guid)); | ||
| 1023 | params.Set("axis", axis); | ||
| 1024 | params.Set("threshold", "0.5"); | ||
| 1025 | if (value > 0) { | ||
| 1026 | params.Set("direction", "+"); | ||
| 1027 | } else { | ||
| 1028 | params.Set("direction", "-"); | ||
| 1029 | } | ||
| 1030 | return params; | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) { | ||
| 1034 | Common::ParamPackage params({{"engine", "sdl"}}); | ||
| 1035 | params.Set("port", port); | ||
| 1036 | params.Set("guid", std::move(guid)); | ||
| 1037 | params.Set("button", button); | ||
| 1038 | params.Set("toggle", false); | ||
| 1039 | return params; | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) { | ||
| 1043 | Common::ParamPackage params({{"engine", "sdl"}}); | ||
| 1044 | |||
| 1045 | params.Set("port", port); | ||
| 1046 | params.Set("guid", std::move(guid)); | ||
| 1047 | params.Set("hat", hat); | ||
| 1048 | switch (value) { | ||
| 1049 | case SDL_HAT_UP: | ||
| 1050 | params.Set("direction", "up"); | ||
| 1051 | break; | ||
| 1052 | case SDL_HAT_DOWN: | ||
| 1053 | params.Set("direction", "down"); | ||
| 1054 | break; | ||
| 1055 | case SDL_HAT_LEFT: | ||
| 1056 | params.Set("direction", "left"); | ||
| 1057 | break; | ||
| 1058 | case SDL_HAT_RIGHT: | ||
| 1059 | params.Set("direction", "right"); | ||
| 1060 | break; | ||
| 1061 | default: | ||
| 1062 | return {}; | ||
| 1063 | } | ||
| 1064 | return params; | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | Common::ParamPackage BuildMotionParam(int port, std::string guid) { | ||
| 1068 | Common::ParamPackage params({{"engine", "sdl"}, {"motion", "0"}}); | ||
| 1069 | params.Set("port", port); | ||
| 1070 | params.Set("guid", std::move(guid)); | ||
| 1071 | return params; | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { | ||
| 1075 | switch (event.type) { | ||
| 1076 | case SDL_JOYAXISMOTION: { | ||
| 1077 | if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { | ||
| 1078 | return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), | ||
| 1079 | static_cast<s32>(event.jaxis.axis), | ||
| 1080 | event.jaxis.value); | ||
| 1081 | } | ||
| 1082 | break; | ||
| 1083 | } | ||
| 1084 | case SDL_JOYBUTTONUP: { | ||
| 1085 | if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) { | ||
| 1086 | return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), | ||
| 1087 | static_cast<s32>(event.jbutton.button)); | ||
| 1088 | } | ||
| 1089 | break; | ||
| 1090 | } | ||
| 1091 | case SDL_JOYHATMOTION: { | ||
| 1092 | if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) { | ||
| 1093 | return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), | ||
| 1094 | static_cast<s32>(event.jhat.hat), | ||
| 1095 | static_cast<s32>(event.jhat.value)); | ||
| 1096 | } | ||
| 1097 | break; | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | return {}; | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) { | ||
| 1104 | switch (event.type) { | ||
| 1105 | case SDL_JOYAXISMOTION: { | ||
| 1106 | if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { | ||
| 1107 | return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), | ||
| 1108 | static_cast<s32>(event.jaxis.axis), | ||
| 1109 | event.jaxis.value); | ||
| 1110 | } | ||
| 1111 | break; | ||
| 1112 | } | ||
| 1113 | case SDL_JOYBUTTONUP: { | ||
| 1114 | if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) { | ||
| 1115 | return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), | ||
| 1116 | static_cast<s32>(event.jbutton.button)); | ||
| 1117 | } | ||
| 1118 | break; | ||
| 1119 | } | ||
| 1120 | case SDL_JOYHATMOTION: { | ||
| 1121 | if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) { | ||
| 1122 | return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), | ||
| 1123 | static_cast<s32>(event.jhat.hat), | ||
| 1124 | static_cast<s32>(event.jhat.value)); | ||
| 1125 | } | ||
| 1126 | break; | ||
| 1127 | } | ||
| 1128 | case SDL_CONTROLLERSENSORUPDATE: { | ||
| 1129 | bool is_motion_shaking = false; | ||
| 1130 | constexpr float gyro_threshold = 5.0f; | ||
| 1131 | constexpr float accel_threshold = 11.0f; | ||
| 1132 | if (event.csensor.sensor == SDL_SENSOR_ACCEL) { | ||
| 1133 | const Common::Vec3f acceleration = {-event.csensor.data[0], event.csensor.data[2], | ||
| 1134 | -event.csensor.data[1]}; | ||
| 1135 | if (acceleration.Length() > accel_threshold) { | ||
| 1136 | is_motion_shaking = true; | ||
| 1137 | } | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | if (event.csensor.sensor == SDL_SENSOR_GYRO) { | ||
| 1141 | const Common::Vec3f gyroscope = {event.csensor.data[0], -event.csensor.data[2], | ||
| 1142 | event.csensor.data[1]}; | ||
| 1143 | if (gyroscope.Length() > gyro_threshold) { | ||
| 1144 | is_motion_shaking = true; | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | if (!is_motion_shaking) { | ||
| 1149 | break; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | if (const auto joystick = state.GetSDLJoystickBySDLID(event.csensor.which)) { | ||
| 1153 | return BuildMotionParam(joystick->GetPort(), joystick->GetGUID()); | ||
| 1154 | } | ||
| 1155 | break; | ||
| 1156 | } | ||
| 1157 | } | ||
| 1158 | return {}; | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, | ||
| 1162 | const SDL_GameControllerButtonBind& binding) { | ||
| 1163 | switch (binding.bindType) { | ||
| 1164 | case SDL_CONTROLLER_BINDTYPE_NONE: | ||
| 1165 | break; | ||
| 1166 | case SDL_CONTROLLER_BINDTYPE_AXIS: | ||
| 1167 | return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); | ||
| 1168 | case SDL_CONTROLLER_BINDTYPE_BUTTON: | ||
| 1169 | return BuildButtonParamPackageForButton(port, guid, binding.value.button); | ||
| 1170 | case SDL_CONTROLLER_BINDTYPE_HAT: | ||
| 1171 | return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat, | ||
| 1172 | binding.value.hat.hat_mask); | ||
| 1173 | } | ||
| 1174 | return {}; | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x, | ||
| 1178 | int axis_y, float offset_x, float offset_y) { | ||
| 1179 | Common::ParamPackage params; | ||
| 1180 | params.Set("engine", "sdl"); | ||
| 1181 | params.Set("port", port); | ||
| 1182 | params.Set("guid", guid); | ||
| 1183 | params.Set("axis_x", axis_x); | ||
| 1184 | params.Set("axis_y", axis_y); | ||
| 1185 | params.Set("offset_x", offset_x); | ||
| 1186 | params.Set("offset_y", offset_y); | ||
| 1187 | params.Set("invert_x", "+"); | ||
| 1188 | params.Set("invert_y", "+"); | ||
| 1189 | return params; | ||
| 1190 | } | ||
| 1191 | } // Anonymous namespace | ||
| 1192 | |||
| 1193 | ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) { | ||
| 1194 | if (!params.Has("guid") || !params.Has("port")) { | ||
| 1195 | return {}; | ||
| 1196 | } | ||
| 1197 | const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); | ||
| 1198 | |||
| 1199 | auto* controller = joystick->GetSDLGameController(); | ||
| 1200 | if (controller == nullptr) { | ||
| 1201 | return {}; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. | ||
| 1205 | // We will add those afterwards | ||
| 1206 | // This list also excludes Screenshot since theres not really a mapping for that | ||
| 1207 | ButtonBindings switch_to_sdl_button; | ||
| 1208 | |||
| 1209 | if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { | ||
| 1210 | switch_to_sdl_button = GetNintendoButtonBinding(joystick); | ||
| 1211 | } else { | ||
| 1212 | switch_to_sdl_button = GetDefaultButtonBinding(); | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | // Add the missing bindings for ZL/ZR | ||
| 1216 | static constexpr ZButtonBindings switch_to_sdl_axis{{ | ||
| 1217 | {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, | ||
| 1218 | {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, | ||
| 1219 | }}; | ||
| 1220 | |||
| 1221 | // Parameters contain two joysticks return dual | ||
| 1222 | if (params.Has("guid2")) { | ||
| 1223 | const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); | ||
| 1224 | |||
| 1225 | if (joystick2->GetSDLGameController() != nullptr) { | ||
| 1226 | return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button, | ||
| 1227 | switch_to_sdl_axis); | ||
| 1228 | } | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis); | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | ButtonBindings SDLState::GetDefaultButtonBinding() const { | ||
| 1235 | return { | ||
| 1236 | std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, | ||
| 1237 | {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, | ||
| 1238 | {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, | ||
| 1239 | {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, | ||
| 1240 | {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, | ||
| 1241 | {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, | ||
| 1242 | {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, | ||
| 1243 | {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, | ||
| 1244 | {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, | ||
| 1245 | {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, | ||
| 1246 | {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, | ||
| 1247 | {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, | ||
| 1248 | {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, | ||
| 1249 | {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, | ||
| 1250 | {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, | ||
| 1251 | {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, | ||
| 1252 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, | ||
| 1253 | }; | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | ButtonBindings SDLState::GetNintendoButtonBinding( | ||
| 1257 | const std::shared_ptr<SDLJoystick>& joystick) const { | ||
| 1258 | // Default SL/SR mapping for pro controllers | ||
| 1259 | auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; | ||
| 1260 | auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; | ||
| 1261 | |||
| 1262 | if (joystick->IsJoyconLeft()) { | ||
| 1263 | sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; | ||
| 1264 | sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; | ||
| 1265 | } | ||
| 1266 | if (joystick->IsJoyconRight()) { | ||
| 1267 | sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; | ||
| 1268 | sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | return { | ||
| 1272 | std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A}, | ||
| 1273 | {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B}, | ||
| 1274 | {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X}, | ||
| 1275 | {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y}, | ||
| 1276 | {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, | ||
| 1277 | {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, | ||
| 1278 | {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, | ||
| 1279 | {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, | ||
| 1280 | {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, | ||
| 1281 | {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, | ||
| 1282 | {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, | ||
| 1283 | {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, | ||
| 1284 | {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, | ||
| 1285 | {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, | ||
| 1286 | {Settings::NativeButton::SL, sl_button}, | ||
| 1287 | {Settings::NativeButton::SR, sr_button}, | ||
| 1288 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, | ||
| 1289 | }; | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | ButtonMapping SDLState::GetSingleControllerMapping( | ||
| 1293 | const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button, | ||
| 1294 | const ZButtonBindings& switch_to_sdl_axis) const { | ||
| 1295 | ButtonMapping mapping; | ||
| 1296 | mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); | ||
| 1297 | auto* controller = joystick->GetSDLGameController(); | ||
| 1298 | |||
| 1299 | for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { | ||
| 1300 | const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); | ||
| 1301 | mapping.insert_or_assign( | ||
| 1302 | switch_button, | ||
| 1303 | BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); | ||
| 1304 | } | ||
| 1305 | for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { | ||
| 1306 | const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); | ||
| 1307 | mapping.insert_or_assign( | ||
| 1308 | switch_button, | ||
| 1309 | BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | return mapping; | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | ButtonMapping SDLState::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, | ||
| 1316 | const std::shared_ptr<SDLJoystick>& joystick2, | ||
| 1317 | const ButtonBindings& switch_to_sdl_button, | ||
| 1318 | const ZButtonBindings& switch_to_sdl_axis) const { | ||
| 1319 | ButtonMapping mapping; | ||
| 1320 | mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); | ||
| 1321 | auto* controller = joystick->GetSDLGameController(); | ||
| 1322 | auto* controller2 = joystick2->GetSDLGameController(); | ||
| 1323 | |||
| 1324 | for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { | ||
| 1325 | if (IsButtonOnLeftSide(switch_button)) { | ||
| 1326 | const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button); | ||
| 1327 | mapping.insert_or_assign( | ||
| 1328 | switch_button, | ||
| 1329 | BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); | ||
| 1330 | continue; | ||
| 1331 | } | ||
| 1332 | const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); | ||
| 1333 | mapping.insert_or_assign( | ||
| 1334 | switch_button, | ||
| 1335 | BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); | ||
| 1336 | } | ||
| 1337 | for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { | ||
| 1338 | if (IsButtonOnLeftSide(switch_button)) { | ||
| 1339 | const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis); | ||
| 1340 | mapping.insert_or_assign( | ||
| 1341 | switch_button, | ||
| 1342 | BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); | ||
| 1343 | continue; | ||
| 1344 | } | ||
| 1345 | const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); | ||
| 1346 | mapping.insert_or_assign( | ||
| 1347 | switch_button, | ||
| 1348 | BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); | ||
| 1349 | } | ||
| 1350 | |||
| 1351 | return mapping; | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | bool SDLState::IsButtonOnLeftSide(Settings::NativeButton::Values button) const { | ||
| 1355 | switch (button) { | ||
| 1356 | case Settings::NativeButton::DDown: | ||
| 1357 | case Settings::NativeButton::DLeft: | ||
| 1358 | case Settings::NativeButton::DRight: | ||
| 1359 | case Settings::NativeButton::DUp: | ||
| 1360 | case Settings::NativeButton::L: | ||
| 1361 | case Settings::NativeButton::LStick: | ||
| 1362 | case Settings::NativeButton::Minus: | ||
| 1363 | case Settings::NativeButton::Screenshot: | ||
| 1364 | case Settings::NativeButton::ZL: | ||
| 1365 | return true; | ||
| 1366 | default: | ||
| 1367 | return false; | ||
| 1368 | } | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) { | ||
| 1372 | if (!params.Has("guid") || !params.Has("port")) { | ||
| 1373 | return {}; | ||
| 1374 | } | ||
| 1375 | const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); | ||
| 1376 | const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); | ||
| 1377 | auto* controller = joystick->GetSDLGameController(); | ||
| 1378 | if (controller == nullptr) { | ||
| 1379 | return {}; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | AnalogMapping mapping = {}; | ||
| 1383 | const auto& binding_left_x = | ||
| 1384 | SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); | ||
| 1385 | const auto& binding_left_y = | ||
| 1386 | SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); | ||
| 1387 | if (params.Has("guid2")) { | ||
| 1388 | joystick2->PreSetAxis(binding_left_x.value.axis); | ||
| 1389 | joystick2->PreSetAxis(binding_left_y.value.axis); | ||
| 1390 | const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0); | ||
| 1391 | const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0); | ||
| 1392 | mapping.insert_or_assign( | ||
| 1393 | Settings::NativeAnalog::LStick, | ||
| 1394 | BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(), | ||
| 1395 | binding_left_x.value.axis, binding_left_y.value.axis, | ||
| 1396 | left_offset_x, left_offset_y)); | ||
| 1397 | } else { | ||
| 1398 | joystick->PreSetAxis(binding_left_x.value.axis); | ||
| 1399 | joystick->PreSetAxis(binding_left_y.value.axis); | ||
| 1400 | const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0); | ||
| 1401 | const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0); | ||
| 1402 | mapping.insert_or_assign( | ||
| 1403 | Settings::NativeAnalog::LStick, | ||
| 1404 | BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), | ||
| 1405 | binding_left_x.value.axis, binding_left_y.value.axis, | ||
| 1406 | left_offset_x, left_offset_y)); | ||
| 1407 | } | ||
| 1408 | const auto& binding_right_x = | ||
| 1409 | SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); | ||
| 1410 | const auto& binding_right_y = | ||
| 1411 | SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); | ||
| 1412 | joystick->PreSetAxis(binding_right_x.value.axis); | ||
| 1413 | joystick->PreSetAxis(binding_right_y.value.axis); | ||
| 1414 | const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0); | ||
| 1415 | const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0); | ||
| 1416 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, | ||
| 1417 | BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), | ||
| 1418 | binding_right_x.value.axis, | ||
| 1419 | binding_right_y.value.axis, right_offset_x, | ||
| 1420 | right_offset_y)); | ||
| 1421 | return mapping; | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& params) { | ||
| 1425 | if (!params.Has("guid") || !params.Has("port")) { | ||
| 1426 | return {}; | ||
| 1427 | } | ||
| 1428 | const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); | ||
| 1429 | const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); | ||
| 1430 | auto* controller = joystick->GetSDLGameController(); | ||
| 1431 | if (controller == nullptr) { | ||
| 1432 | return {}; | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | MotionMapping mapping = {}; | ||
| 1436 | joystick->EnableMotion(); | ||
| 1437 | |||
| 1438 | if (joystick->HasGyro() || joystick->HasAccel()) { | ||
| 1439 | mapping.insert_or_assign(Settings::NativeMotion::MotionRight, | ||
| 1440 | BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); | ||
| 1441 | } | ||
| 1442 | if (params.Has("guid2")) { | ||
| 1443 | joystick2->EnableMotion(); | ||
| 1444 | if (joystick2->HasGyro() || joystick2->HasAccel()) { | ||
| 1445 | mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, | ||
| 1446 | BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID())); | ||
| 1447 | } | ||
| 1448 | } else { | ||
| 1449 | if (joystick->HasGyro() || joystick->HasAccel()) { | ||
| 1450 | mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, | ||
| 1451 | BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); | ||
| 1452 | } | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | return mapping; | ||
| 1456 | } | ||
| 1457 | namespace Polling { | ||
| 1458 | class SDLPoller : public InputCommon::Polling::DevicePoller { | ||
| 1459 | public: | ||
| 1460 | explicit SDLPoller(SDLState& state_) : state(state_) {} | ||
| 1461 | |||
| 1462 | void Start([[maybe_unused]] const std::string& device_id) override { | ||
| 1463 | state.event_queue.Clear(); | ||
| 1464 | state.polling = true; | ||
| 1465 | } | ||
| 1466 | |||
| 1467 | void Stop() override { | ||
| 1468 | state.polling = false; | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | protected: | ||
| 1472 | SDLState& state; | ||
| 1473 | }; | ||
| 1474 | |||
| 1475 | class SDLButtonPoller final : public SDLPoller { | ||
| 1476 | public: | ||
| 1477 | explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {} | ||
| 1478 | |||
| 1479 | Common::ParamPackage GetNextInput() override { | ||
| 1480 | SDL_Event event; | ||
| 1481 | while (state.event_queue.Pop(event)) { | ||
| 1482 | const auto package = FromEvent(event); | ||
| 1483 | if (package) { | ||
| 1484 | return *package; | ||
| 1485 | } | ||
| 1486 | } | ||
| 1487 | return {}; | ||
| 1488 | } | ||
| 1489 | [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) { | ||
| 1490 | switch (event.type) { | ||
| 1491 | case SDL_JOYAXISMOTION: | ||
| 1492 | if (!axis_memory.count(event.jaxis.which) || | ||
| 1493 | !axis_memory[event.jaxis.which].count(event.jaxis.axis)) { | ||
| 1494 | axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value; | ||
| 1495 | axis_event_count[event.jaxis.which][event.jaxis.axis] = 1; | ||
| 1496 | break; | ||
| 1497 | } else { | ||
| 1498 | axis_event_count[event.jaxis.which][event.jaxis.axis]++; | ||
| 1499 | // The joystick and axis exist in our map if we take this branch, so no checks | ||
| 1500 | // needed | ||
| 1501 | if (std::abs( | ||
| 1502 | (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) / | ||
| 1503 | 32767.0) < 0.5) { | ||
| 1504 | break; | ||
| 1505 | } else { | ||
| 1506 | if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 && | ||
| 1507 | IsAxisAtPole(event.jaxis.value) && | ||
| 1508 | IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) { | ||
| 1509 | // If we have exactly two events and both are near a pole, this is | ||
| 1510 | // likely a digital input masquerading as an analog axis; Instead of | ||
| 1511 | // trying to look at the direction the axis travelled, assume the first | ||
| 1512 | // event was press and the second was release; This should handle most | ||
| 1513 | // digital axes while deferring to the direction of travel for analog | ||
| 1514 | // axes | ||
| 1515 | event.jaxis.value = static_cast<Sint16>( | ||
| 1516 | std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis])); | ||
| 1517 | } else { | ||
| 1518 | // There are more than two events, so this is likely a true analog axis, | ||
| 1519 | // check the direction it travelled | ||
| 1520 | event.jaxis.value = static_cast<Sint16>(std::copysign( | ||
| 1521 | 32767, | ||
| 1522 | event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis])); | ||
| 1523 | } | ||
| 1524 | axis_memory.clear(); | ||
| 1525 | axis_event_count.clear(); | ||
| 1526 | } | ||
| 1527 | } | ||
| 1528 | [[fallthrough]]; | ||
| 1529 | case SDL_JOYBUTTONUP: | ||
| 1530 | case SDL_JOYHATMOTION: | ||
| 1531 | return {SDLEventToButtonParamPackage(state, event)}; | ||
| 1532 | } | ||
| 1533 | return std::nullopt; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | private: | ||
| 1537 | // Determine whether an axis value is close to an extreme or center | ||
| 1538 | // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per | ||
| 1539 | // axis, which is why the center must be considered a pole | ||
| 1540 | bool IsAxisAtPole(int16_t value) const { | ||
| 1541 | return std::abs(value) >= 32767 || std::abs(value) < 327; | ||
| 1542 | } | ||
| 1543 | std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory; | ||
| 1544 | std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count; | ||
| 1545 | }; | ||
| 1546 | |||
| 1547 | class SDLMotionPoller final : public SDLPoller { | ||
| 1548 | public: | ||
| 1549 | explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {} | ||
| 1550 | |||
| 1551 | Common::ParamPackage GetNextInput() override { | ||
| 1552 | SDL_Event event; | ||
| 1553 | while (state.event_queue.Pop(event)) { | ||
| 1554 | const auto package = FromEvent(event); | ||
| 1555 | if (package) { | ||
| 1556 | return *package; | ||
| 1557 | } | ||
| 1558 | } | ||
| 1559 | return {}; | ||
| 1560 | } | ||
| 1561 | [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const { | ||
| 1562 | switch (event.type) { | ||
| 1563 | case SDL_JOYAXISMOTION: | ||
| 1564 | if (std::abs(event.jaxis.value / 32767.0) < 0.5) { | ||
| 1565 | break; | ||
| 1566 | } | ||
| 1567 | [[fallthrough]]; | ||
| 1568 | case SDL_JOYBUTTONUP: | ||
| 1569 | case SDL_JOYHATMOTION: | ||
| 1570 | case SDL_CONTROLLERSENSORUPDATE: | ||
| 1571 | return {SDLEventToMotionParamPackage(state, event)}; | ||
| 1572 | } | ||
| 1573 | return std::nullopt; | ||
| 1574 | } | ||
| 1575 | }; | ||
| 1576 | |||
| 1577 | /** | ||
| 1578 | * Attempts to match the press to a controller joy axis (left/right stick) and if a match | ||
| 1579 | * isn't found, checks if the event matches anything from SDLButtonPoller and uses that | ||
| 1580 | * instead | ||
| 1581 | */ | ||
| 1582 | class SDLAnalogPreferredPoller final : public SDLPoller { | ||
| 1583 | public: | ||
| 1584 | explicit SDLAnalogPreferredPoller(SDLState& state_) | ||
| 1585 | : SDLPoller(state_), button_poller(state_) {} | ||
| 1586 | |||
| 1587 | void Start(const std::string& device_id) override { | ||
| 1588 | SDLPoller::Start(device_id); | ||
| 1589 | // Reset stored axes | ||
| 1590 | first_axis = -1; | ||
| 1591 | } | ||
| 1592 | |||
| 1593 | Common::ParamPackage GetNextInput() override { | ||
| 1594 | SDL_Event event; | ||
| 1595 | while (state.event_queue.Pop(event)) { | ||
| 1596 | if (event.type != SDL_JOYAXISMOTION) { | ||
| 1597 | // Check for a button press | ||
| 1598 | auto button_press = button_poller.FromEvent(event); | ||
| 1599 | if (button_press) { | ||
| 1600 | return *button_press; | ||
| 1601 | } | ||
| 1602 | continue; | ||
| 1603 | } | ||
| 1604 | const auto axis = event.jaxis.axis; | ||
| 1605 | |||
| 1606 | // Filter out axis events that are below a threshold | ||
| 1607 | if (std::abs(event.jaxis.value / 32767.0) < 0.5) { | ||
| 1608 | continue; | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | // Filter out axis events that are the same | ||
| 1612 | if (first_axis == axis) { | ||
| 1613 | continue; | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | // In order to return a complete analog param, we need inputs for both axes. | ||
| 1617 | // If the first axis isn't set we set the value then wait till next event | ||
| 1618 | if (first_axis == -1) { | ||
| 1619 | first_axis = axis; | ||
| 1620 | continue; | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { | ||
| 1624 | // Set offset to zero since the joystick is not on center | ||
| 1625 | auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), | ||
| 1626 | first_axis, axis, 0, 0); | ||
| 1627 | first_axis = -1; | ||
| 1628 | return params; | ||
| 1629 | } | ||
| 1630 | } | ||
| 1631 | return {}; | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | private: | ||
| 1635 | int first_axis = -1; | ||
| 1636 | SDLButtonPoller button_poller; | ||
| 1637 | }; | ||
| 1638 | } // namespace Polling | ||
| 1639 | |||
| 1640 | SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) { | ||
| 1641 | Pollers pollers; | ||
| 1642 | |||
| 1643 | switch (type) { | ||
| 1644 | case InputCommon::Polling::DeviceType::AnalogPreferred: | ||
| 1645 | pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this)); | ||
| 1646 | break; | ||
| 1647 | case InputCommon::Polling::DeviceType::Button: | ||
| 1648 | pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); | ||
| 1649 | break; | ||
| 1650 | case InputCommon::Polling::DeviceType::Motion: | ||
| 1651 | pollers.emplace_back(std::make_unique<Polling::SDLMotionPoller>(*this)); | ||
| 1652 | break; | ||
| 1653 | } | ||
| 1654 | |||
| 1655 | return pollers; | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | } // namespace InputCommon::SDL | ||
diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp deleted file mode 100644 index 1598092b6..000000000 --- a/src/input_common/tas/tas_input.cpp +++ /dev/null | |||
| @@ -1,455 +0,0 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <regex> | ||
| 7 | |||
| 8 | #include "common/fs/file.h" | ||
| 9 | #include "common/fs/fs_types.h" | ||
| 10 | #include "common/fs/path_util.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "common/settings.h" | ||
| 13 | #include "input_common/tas/tas_input.h" | ||
| 14 | |||
| 15 | namespace TasInput { | ||
| 16 | |||
| 17 | // Supported keywords and buttons from a TAS file | ||
| 18 | constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = { | ||
| 19 | std::pair{"KEY_A", TasButton::BUTTON_A}, | ||
| 20 | {"KEY_B", TasButton::BUTTON_B}, | ||
| 21 | {"KEY_X", TasButton::BUTTON_X}, | ||
| 22 | {"KEY_Y", TasButton::BUTTON_Y}, | ||
| 23 | {"KEY_LSTICK", TasButton::STICK_L}, | ||
| 24 | {"KEY_RSTICK", TasButton::STICK_R}, | ||
| 25 | {"KEY_L", TasButton::TRIGGER_L}, | ||
| 26 | {"KEY_R", TasButton::TRIGGER_R}, | ||
| 27 | {"KEY_PLUS", TasButton::BUTTON_PLUS}, | ||
| 28 | {"KEY_MINUS", TasButton::BUTTON_MINUS}, | ||
| 29 | {"KEY_DLEFT", TasButton::BUTTON_LEFT}, | ||
| 30 | {"KEY_DUP", TasButton::BUTTON_UP}, | ||
| 31 | {"KEY_DRIGHT", TasButton::BUTTON_RIGHT}, | ||
| 32 | {"KEY_DDOWN", TasButton::BUTTON_DOWN}, | ||
| 33 | {"KEY_SL", TasButton::BUTTON_SL}, | ||
| 34 | {"KEY_SR", TasButton::BUTTON_SR}, | ||
| 35 | {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE}, | ||
| 36 | {"KEY_HOME", TasButton::BUTTON_HOME}, | ||
| 37 | {"KEY_ZL", TasButton::TRIGGER_ZL}, | ||
| 38 | {"KEY_ZR", TasButton::TRIGGER_ZR}, | ||
| 39 | }; | ||
| 40 | |||
| 41 | Tas::Tas() { | ||
| 42 | if (!Settings::values.tas_enable) { | ||
| 43 | needs_reset = true; | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | LoadTasFiles(); | ||
| 47 | } | ||
| 48 | |||
| 49 | Tas::~Tas() { | ||
| 50 | Stop(); | ||
| 51 | }; | ||
| 52 | |||
| 53 | void Tas::LoadTasFiles() { | ||
| 54 | script_length = 0; | ||
| 55 | for (size_t i = 0; i < commands.size(); i++) { | ||
| 56 | LoadTasFile(i); | ||
| 57 | if (commands[i].size() > script_length) { | ||
| 58 | script_length = commands[i].size(); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | void Tas::LoadTasFile(size_t player_index) { | ||
| 64 | if (!commands[player_index].empty()) { | ||
| 65 | commands[player_index].clear(); | ||
| 66 | } | ||
| 67 | std::string file = | ||
| 68 | Common::FS::ReadStringFromFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / | ||
| 69 | fmt::format("script0-{}.txt", player_index + 1), | ||
| 70 | Common::FS::FileType::BinaryFile); | ||
| 71 | std::stringstream command_line(file); | ||
| 72 | std::string line; | ||
| 73 | int frame_no = 0; | ||
| 74 | while (std::getline(command_line, line, '\n')) { | ||
| 75 | if (line.empty()) { | ||
| 76 | continue; | ||
| 77 | } | ||
| 78 | LOG_DEBUG(Input, "Loading line: {}", line); | ||
| 79 | std::smatch m; | ||
| 80 | |||
| 81 | std::stringstream linestream(line); | ||
| 82 | std::string segment; | ||
| 83 | std::vector<std::string> seglist; | ||
| 84 | |||
| 85 | while (std::getline(linestream, segment, ' ')) { | ||
| 86 | seglist.push_back(segment); | ||
| 87 | } | ||
| 88 | |||
| 89 | if (seglist.size() < 4) { | ||
| 90 | continue; | ||
| 91 | } | ||
| 92 | |||
| 93 | while (frame_no < std::stoi(seglist.at(0))) { | ||
| 94 | commands[player_index].push_back({}); | ||
| 95 | frame_no++; | ||
| 96 | } | ||
| 97 | |||
| 98 | TASCommand command = { | ||
| 99 | .buttons = ReadCommandButtons(seglist.at(1)), | ||
| 100 | .l_axis = ReadCommandAxis(seglist.at(2)), | ||
| 101 | .r_axis = ReadCommandAxis(seglist.at(3)), | ||
| 102 | }; | ||
| 103 | commands[player_index].push_back(command); | ||
| 104 | frame_no++; | ||
| 105 | } | ||
| 106 | LOG_INFO(Input, "TAS file loaded! {} frames", frame_no); | ||
| 107 | } | ||
| 108 | |||
| 109 | void Tas::WriteTasFile(std::u8string file_name) { | ||
| 110 | std::string output_text; | ||
| 111 | for (size_t frame = 0; frame < record_commands.size(); frame++) { | ||
| 112 | if (!output_text.empty()) { | ||
| 113 | output_text += "\n"; | ||
| 114 | } | ||
| 115 | const TASCommand& line = record_commands[frame]; | ||
| 116 | output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " + | ||
| 117 | WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis); | ||
| 118 | } | ||
| 119 | const auto bytes_written = Common::FS::WriteStringToFile( | ||
| 120 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name, | ||
| 121 | Common::FS::FileType::TextFile, output_text); | ||
| 122 | if (bytes_written == output_text.size()) { | ||
| 123 | LOG_INFO(Input, "TAS file written to file!"); | ||
| 124 | } else { | ||
| 125 | LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written, | ||
| 126 | output_text.size()); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | std::pair<float, float> Tas::FlipAxisY(std::pair<float, float> old) { | ||
| 131 | auto [x, y] = old; | ||
| 132 | return {x, -y}; | ||
| 133 | } | ||
| 134 | |||
| 135 | void Tas::RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes) { | ||
| 136 | last_input = {buttons, FlipAxisY(axes[0]), FlipAxisY(axes[1])}; | ||
| 137 | } | ||
| 138 | |||
| 139 | std::tuple<TasState, size_t, size_t> Tas::GetStatus() const { | ||
| 140 | TasState state; | ||
| 141 | if (is_recording) { | ||
| 142 | return {TasState::Recording, 0, record_commands.size()}; | ||
| 143 | } | ||
| 144 | |||
| 145 | if (is_running) { | ||
| 146 | state = TasState::Running; | ||
| 147 | } else { | ||
| 148 | state = TasState::Stopped; | ||
| 149 | } | ||
| 150 | |||
| 151 | return {state, current_command, script_length}; | ||
| 152 | } | ||
| 153 | |||
| 154 | std::string Tas::DebugButtons(u32 buttons) const { | ||
| 155 | return fmt::format("{{ {} }}", TasInput::Tas::ButtonsToString(buttons)); | ||
| 156 | } | ||
| 157 | |||
| 158 | std::string Tas::DebugJoystick(float x, float y) const { | ||
| 159 | return fmt::format("[ {} , {} ]", std::to_string(x), std::to_string(y)); | ||
| 160 | } | ||
| 161 | |||
| 162 | std::string Tas::DebugInput(const TasData& data) const { | ||
| 163 | return fmt::format("{{ {} , {} , {} }}", DebugButtons(data.buttons), | ||
| 164 | DebugJoystick(data.axis[0], data.axis[1]), | ||
| 165 | DebugJoystick(data.axis[2], data.axis[3])); | ||
| 166 | } | ||
| 167 | |||
| 168 | std::string Tas::DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const { | ||
| 169 | std::string returns = "[ "; | ||
| 170 | for (size_t i = 0; i < arr.size(); i++) { | ||
| 171 | returns += DebugInput(arr[i]); | ||
| 172 | if (i != arr.size() - 1) { | ||
| 173 | returns += " , "; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | return returns + "]"; | ||
| 177 | } | ||
| 178 | |||
| 179 | std::string Tas::ButtonsToString(u32 button) const { | ||
| 180 | std::string returns; | ||
| 181 | for (auto [text_button, tas_button] : text_to_tas_button) { | ||
| 182 | if ((button & static_cast<u32>(tas_button)) != 0) | ||
| 183 | returns += fmt::format(", {}", text_button.substr(4)); | ||
| 184 | } | ||
| 185 | return returns.empty() ? "" : returns.substr(2); | ||
| 186 | } | ||
| 187 | |||
| 188 | void Tas::UpdateThread() { | ||
| 189 | if (!Settings::values.tas_enable) { | ||
| 190 | if (is_running) { | ||
| 191 | Stop(); | ||
| 192 | } | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 | if (is_recording) { | ||
| 197 | record_commands.push_back(last_input); | ||
| 198 | } | ||
| 199 | if (needs_reset) { | ||
| 200 | current_command = 0; | ||
| 201 | needs_reset = false; | ||
| 202 | LoadTasFiles(); | ||
| 203 | LOG_DEBUG(Input, "tas_reset done"); | ||
| 204 | } | ||
| 205 | |||
| 206 | if (!is_running) { | ||
| 207 | tas_data.fill({}); | ||
| 208 | return; | ||
| 209 | } | ||
| 210 | if (current_command < script_length) { | ||
| 211 | LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length); | ||
| 212 | size_t frame = current_command++; | ||
| 213 | for (size_t i = 0; i < commands.size(); i++) { | ||
| 214 | if (frame < commands[i].size()) { | ||
| 215 | TASCommand command = commands[i][frame]; | ||
| 216 | tas_data[i].buttons = command.buttons; | ||
| 217 | auto [l_axis_x, l_axis_y] = command.l_axis; | ||
| 218 | tas_data[i].axis[0] = l_axis_x; | ||
| 219 | tas_data[i].axis[1] = l_axis_y; | ||
| 220 | auto [r_axis_x, r_axis_y] = command.r_axis; | ||
| 221 | tas_data[i].axis[2] = r_axis_x; | ||
| 222 | tas_data[i].axis[3] = r_axis_y; | ||
| 223 | } else { | ||
| 224 | tas_data[i] = {}; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } else { | ||
| 228 | is_running = Settings::values.tas_loop.GetValue(); | ||
| 229 | current_command = 0; | ||
| 230 | tas_data.fill({}); | ||
| 231 | if (!is_running) { | ||
| 232 | SwapToStoredController(); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data)); | ||
| 236 | } | ||
| 237 | |||
| 238 | TasAnalog Tas::ReadCommandAxis(const std::string& line) const { | ||
| 239 | std::stringstream linestream(line); | ||
| 240 | std::string segment; | ||
| 241 | std::vector<std::string> seglist; | ||
| 242 | |||
| 243 | while (std::getline(linestream, segment, ';')) { | ||
| 244 | seglist.push_back(segment); | ||
| 245 | } | ||
| 246 | |||
| 247 | const float x = std::stof(seglist.at(0)) / 32767.0f; | ||
| 248 | const float y = std::stof(seglist.at(1)) / 32767.0f; | ||
| 249 | |||
| 250 | return {x, y}; | ||
| 251 | } | ||
| 252 | |||
| 253 | u32 Tas::ReadCommandButtons(const std::string& data) const { | ||
| 254 | std::stringstream button_text(data); | ||
| 255 | std::string line; | ||
| 256 | u32 buttons = 0; | ||
| 257 | while (std::getline(button_text, line, ';')) { | ||
| 258 | for (auto [text, tas_button] : text_to_tas_button) { | ||
| 259 | if (text == line) { | ||
| 260 | buttons |= static_cast<u32>(tas_button); | ||
| 261 | break; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | return buttons; | ||
| 266 | } | ||
| 267 | |||
| 268 | std::string Tas::WriteCommandAxis(TasAnalog data) const { | ||
| 269 | auto [x, y] = data; | ||
| 270 | std::string line; | ||
| 271 | line += std::to_string(static_cast<int>(x * 32767)); | ||
| 272 | line += ";"; | ||
| 273 | line += std::to_string(static_cast<int>(y * 32767)); | ||
| 274 | return line; | ||
| 275 | } | ||
| 276 | |||
| 277 | std::string Tas::WriteCommandButtons(u32 data) const { | ||
| 278 | if (data == 0) { | ||
| 279 | return "NONE"; | ||
| 280 | } | ||
| 281 | |||
| 282 | std::string line; | ||
| 283 | u32 index = 0; | ||
| 284 | while (data > 0) { | ||
| 285 | if ((data & 1) == 1) { | ||
| 286 | for (auto [text, tas_button] : text_to_tas_button) { | ||
| 287 | if (tas_button == static_cast<TasButton>(1 << index)) { | ||
| 288 | if (line.size() > 0) { | ||
| 289 | line += ";"; | ||
| 290 | } | ||
| 291 | line += text; | ||
| 292 | break; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
| 296 | index++; | ||
| 297 | data >>= 1; | ||
| 298 | } | ||
| 299 | return line; | ||
| 300 | } | ||
| 301 | |||
| 302 | void Tas::StartStop() { | ||
| 303 | if (!Settings::values.tas_enable) { | ||
| 304 | return; | ||
| 305 | } | ||
| 306 | if (is_running) { | ||
| 307 | Stop(); | ||
| 308 | } else { | ||
| 309 | is_running = true; | ||
| 310 | SwapToTasController(); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | void Tas::Stop() { | ||
| 315 | is_running = false; | ||
| 316 | SwapToStoredController(); | ||
| 317 | } | ||
| 318 | |||
| 319 | void Tas::SwapToTasController() { | ||
| 320 | if (!Settings::values.tas_swap_controllers) { | ||
| 321 | return; | ||
| 322 | } | ||
| 323 | auto& players = Settings::values.players.GetValue(); | ||
| 324 | for (std::size_t index = 0; index < players.size(); index++) { | ||
| 325 | auto& player = players[index]; | ||
| 326 | player_mappings[index] = player; | ||
| 327 | |||
| 328 | // Only swap active controllers | ||
| 329 | if (!player.connected) { | ||
| 330 | continue; | ||
| 331 | } | ||
| 332 | |||
| 333 | Common::ParamPackage tas_param; | ||
| 334 | tas_param.Set("pad", static_cast<u8>(index)); | ||
| 335 | auto button_mapping = GetButtonMappingForDevice(tas_param); | ||
| 336 | auto analog_mapping = GetAnalogMappingForDevice(tas_param); | ||
| 337 | auto& buttons = player.buttons; | ||
| 338 | auto& analogs = player.analogs; | ||
| 339 | |||
| 340 | for (std::size_t i = 0; i < buttons.size(); ++i) { | ||
| 341 | buttons[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)].Serialize(); | ||
| 342 | } | ||
| 343 | for (std::size_t i = 0; i < analogs.size(); ++i) { | ||
| 344 | analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize(); | ||
| 345 | } | ||
| 346 | } | ||
| 347 | is_old_input_saved = true; | ||
| 348 | Settings::values.is_device_reload_pending.store(true); | ||
| 349 | } | ||
| 350 | |||
| 351 | void Tas::SwapToStoredController() { | ||
| 352 | if (!is_old_input_saved) { | ||
| 353 | return; | ||
| 354 | } | ||
| 355 | auto& players = Settings::values.players.GetValue(); | ||
| 356 | for (std::size_t index = 0; index < players.size(); index++) { | ||
| 357 | players[index] = player_mappings[index]; | ||
| 358 | } | ||
| 359 | is_old_input_saved = false; | ||
| 360 | Settings::values.is_device_reload_pending.store(true); | ||
| 361 | } | ||
| 362 | |||
| 363 | void Tas::Reset() { | ||
| 364 | if (!Settings::values.tas_enable) { | ||
| 365 | return; | ||
| 366 | } | ||
| 367 | needs_reset = true; | ||
| 368 | } | ||
| 369 | |||
| 370 | bool Tas::Record() { | ||
| 371 | if (!Settings::values.tas_enable) { | ||
| 372 | return true; | ||
| 373 | } | ||
| 374 | is_recording = !is_recording; | ||
| 375 | return is_recording; | ||
| 376 | } | ||
| 377 | |||
| 378 | void Tas::SaveRecording(bool overwrite_file) { | ||
| 379 | if (is_recording) { | ||
| 380 | return; | ||
| 381 | } | ||
| 382 | if (record_commands.empty()) { | ||
| 383 | return; | ||
| 384 | } | ||
| 385 | WriteTasFile(u8"record.txt"); | ||
| 386 | if (overwrite_file) { | ||
| 387 | WriteTasFile(u8"script0-1.txt"); | ||
| 388 | } | ||
| 389 | needs_reset = true; | ||
| 390 | record_commands.clear(); | ||
| 391 | } | ||
| 392 | |||
| 393 | InputCommon::ButtonMapping Tas::GetButtonMappingForDevice( | ||
| 394 | const Common::ParamPackage& params) const { | ||
| 395 | // This list is missing ZL/ZR since those are not considered buttons. | ||
| 396 | // We will add those afterwards | ||
| 397 | // This list also excludes any button that can't be really mapped | ||
| 398 | static constexpr std::array<std::pair<Settings::NativeButton::Values, TasButton>, 20> | ||
| 399 | switch_to_tas_button = { | ||
| 400 | std::pair{Settings::NativeButton::A, TasButton::BUTTON_A}, | ||
| 401 | {Settings::NativeButton::B, TasButton::BUTTON_B}, | ||
| 402 | {Settings::NativeButton::X, TasButton::BUTTON_X}, | ||
| 403 | {Settings::NativeButton::Y, TasButton::BUTTON_Y}, | ||
| 404 | {Settings::NativeButton::LStick, TasButton::STICK_L}, | ||
| 405 | {Settings::NativeButton::RStick, TasButton::STICK_R}, | ||
| 406 | {Settings::NativeButton::L, TasButton::TRIGGER_L}, | ||
| 407 | {Settings::NativeButton::R, TasButton::TRIGGER_R}, | ||
| 408 | {Settings::NativeButton::Plus, TasButton::BUTTON_PLUS}, | ||
| 409 | {Settings::NativeButton::Minus, TasButton::BUTTON_MINUS}, | ||
| 410 | {Settings::NativeButton::DLeft, TasButton::BUTTON_LEFT}, | ||
| 411 | {Settings::NativeButton::DUp, TasButton::BUTTON_UP}, | ||
| 412 | {Settings::NativeButton::DRight, TasButton::BUTTON_RIGHT}, | ||
| 413 | {Settings::NativeButton::DDown, TasButton::BUTTON_DOWN}, | ||
| 414 | {Settings::NativeButton::SL, TasButton::BUTTON_SL}, | ||
| 415 | {Settings::NativeButton::SR, TasButton::BUTTON_SR}, | ||
| 416 | {Settings::NativeButton::Screenshot, TasButton::BUTTON_CAPTURE}, | ||
| 417 | {Settings::NativeButton::Home, TasButton::BUTTON_HOME}, | ||
| 418 | {Settings::NativeButton::ZL, TasButton::TRIGGER_ZL}, | ||
| 419 | {Settings::NativeButton::ZR, TasButton::TRIGGER_ZR}, | ||
| 420 | }; | ||
| 421 | |||
| 422 | InputCommon::ButtonMapping mapping{}; | ||
| 423 | for (const auto& [switch_button, tas_button] : switch_to_tas_button) { | ||
| 424 | Common::ParamPackage button_params({{"engine", "tas"}}); | ||
| 425 | button_params.Set("pad", params.Get("pad", 0)); | ||
| 426 | button_params.Set("button", static_cast<int>(tas_button)); | ||
| 427 | mapping.insert_or_assign(switch_button, std::move(button_params)); | ||
| 428 | } | ||
| 429 | |||
| 430 | return mapping; | ||
| 431 | } | ||
| 432 | |||
| 433 | InputCommon::AnalogMapping Tas::GetAnalogMappingForDevice( | ||
| 434 | const Common::ParamPackage& params) const { | ||
| 435 | |||
| 436 | InputCommon::AnalogMapping mapping = {}; | ||
| 437 | Common::ParamPackage left_analog_params; | ||
| 438 | left_analog_params.Set("engine", "tas"); | ||
| 439 | left_analog_params.Set("pad", params.Get("pad", 0)); | ||
| 440 | left_analog_params.Set("axis_x", static_cast<int>(TasAxes::StickX)); | ||
| 441 | left_analog_params.Set("axis_y", static_cast<int>(TasAxes::StickY)); | ||
| 442 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); | ||
| 443 | Common::ParamPackage right_analog_params; | ||
| 444 | right_analog_params.Set("engine", "tas"); | ||
| 445 | right_analog_params.Set("pad", params.Get("pad", 0)); | ||
| 446 | right_analog_params.Set("axis_x", static_cast<int>(TasAxes::SubstickX)); | ||
| 447 | right_analog_params.Set("axis_y", static_cast<int>(TasAxes::SubstickY)); | ||
| 448 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params)); | ||
| 449 | return mapping; | ||
| 450 | } | ||
| 451 | |||
| 452 | const TasData& Tas::GetTasState(std::size_t pad) const { | ||
| 453 | return tas_data[pad]; | ||
| 454 | } | ||
| 455 | } // namespace TasInput | ||
diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h deleted file mode 100644 index 3e2db8f00..000000000 --- a/src/input_common/tas/tas_input.h +++ /dev/null | |||
| @@ -1,237 +0,0 @@ | |||
| 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 | #include "common/settings_input.h" | ||
| 11 | #include "core/frontend/input.h" | ||
| 12 | #include "input_common/main.h" | ||
| 13 | |||
| 14 | /* | ||
| 15 | To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below | ||
| 16 | Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt | ||
| 17 | for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players). | ||
| 18 | |||
| 19 | A script file has the same format as TAS-nx uses, so final files will look like this: | ||
| 20 | |||
| 21 | 1 KEY_B 0;0 0;0 | ||
| 22 | 6 KEY_ZL 0;0 0;0 | ||
| 23 | 41 KEY_ZL;KEY_Y 0;0 0;0 | ||
| 24 | 43 KEY_X;KEY_A 32767;0 0;0 | ||
| 25 | 44 KEY_A 32767;0 0;0 | ||
| 26 | 45 KEY_A 32767;0 0;0 | ||
| 27 | 46 KEY_A 32767;0 0;0 | ||
| 28 | 47 KEY_A 32767;0 0;0 | ||
| 29 | |||
| 30 | After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey | ||
| 31 | CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file | ||
| 32 | has. Playback can be started or stopped using CTRL+F5. | ||
| 33 | |||
| 34 | However, for playback to actually work, the correct input device has to be selected: In the Controls | ||
| 35 | menu, select TAS from the device list for the controller that the script should be played on. | ||
| 36 | |||
| 37 | Recording a new script file is really simple: Just make sure that the proper device (not TAS) is | ||
| 38 | connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke | ||
| 39 | again (CTRL+F7). The new script will be saved at the location previously selected, as the filename | ||
| 40 | record.txt. | ||
| 41 | |||
| 42 | For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller | ||
| 43 | P1). | ||
| 44 | */ | ||
| 45 | |||
| 46 | namespace TasInput { | ||
| 47 | |||
| 48 | constexpr size_t PLAYER_NUMBER = 8; | ||
| 49 | |||
| 50 | using TasAnalog = std::pair<float, float>; | ||
| 51 | |||
| 52 | enum class TasState { | ||
| 53 | Running, | ||
| 54 | Recording, | ||
| 55 | Stopped, | ||
| 56 | }; | ||
| 57 | |||
| 58 | enum class TasButton : u32 { | ||
| 59 | BUTTON_A = 1U << 0, | ||
| 60 | BUTTON_B = 1U << 1, | ||
| 61 | BUTTON_X = 1U << 2, | ||
| 62 | BUTTON_Y = 1U << 3, | ||
| 63 | STICK_L = 1U << 4, | ||
| 64 | STICK_R = 1U << 5, | ||
| 65 | TRIGGER_L = 1U << 6, | ||
| 66 | TRIGGER_R = 1U << 7, | ||
| 67 | TRIGGER_ZL = 1U << 8, | ||
| 68 | TRIGGER_ZR = 1U << 9, | ||
| 69 | BUTTON_PLUS = 1U << 10, | ||
| 70 | BUTTON_MINUS = 1U << 11, | ||
| 71 | BUTTON_LEFT = 1U << 12, | ||
| 72 | BUTTON_UP = 1U << 13, | ||
| 73 | BUTTON_RIGHT = 1U << 14, | ||
| 74 | BUTTON_DOWN = 1U << 15, | ||
| 75 | BUTTON_SL = 1U << 16, | ||
| 76 | BUTTON_SR = 1U << 17, | ||
| 77 | BUTTON_HOME = 1U << 18, | ||
| 78 | BUTTON_CAPTURE = 1U << 19, | ||
| 79 | }; | ||
| 80 | |||
| 81 | enum class TasAxes : u8 { | ||
| 82 | StickX, | ||
| 83 | StickY, | ||
| 84 | SubstickX, | ||
| 85 | SubstickY, | ||
| 86 | Undefined, | ||
| 87 | }; | ||
| 88 | |||
| 89 | struct TasData { | ||
| 90 | u32 buttons{}; | ||
| 91 | std::array<float, 4> axis{}; | ||
| 92 | }; | ||
| 93 | |||
| 94 | class Tas { | ||
| 95 | public: | ||
| 96 | Tas(); | ||
| 97 | ~Tas(); | ||
| 98 | |||
| 99 | // Changes the input status that will be stored in each frame | ||
| 100 | void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes); | ||
| 101 | |||
| 102 | // Main loop that records or executes input | ||
| 103 | void UpdateThread(); | ||
| 104 | |||
| 105 | // Sets the flag to start or stop the TAS command excecution and swaps controllers profiles | ||
| 106 | void StartStop(); | ||
| 107 | |||
| 108 | // Stop the TAS and reverts any controller profile | ||
| 109 | void Stop(); | ||
| 110 | |||
| 111 | // Sets the flag to reload the file and start from the begining in the next update | ||
| 112 | void Reset(); | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Sets the flag to enable or disable recording of inputs | ||
| 116 | * @return Returns true if the current recording status is enabled | ||
| 117 | */ | ||
| 118 | bool Record(); | ||
| 119 | |||
| 120 | // Saves contents of record_commands on a file if overwrite is enabled player 1 will be | ||
| 121 | // overwritten with the recorded commands | ||
| 122 | void SaveRecording(bool overwrite_file); | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Returns the current status values of TAS playback/recording | ||
| 126 | * @return Tuple of | ||
| 127 | * TasState indicating the current state out of Running, Recording or Stopped ; | ||
| 128 | * Current playback progress or amount of frames (so far) for Recording ; | ||
| 129 | * Total length of script file currently loaded or amount of frames (so far) for Recording | ||
| 130 | */ | ||
| 131 | std::tuple<TasState, size_t, size_t> GetStatus() const; | ||
| 132 | |||
| 133 | // Retuns an array of the default button mappings | ||
| 134 | InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; | ||
| 135 | |||
| 136 | // Retuns an array of the default analog mappings | ||
| 137 | InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; | ||
| 138 | [[nodiscard]] const TasData& GetTasState(std::size_t pad) const; | ||
| 139 | |||
| 140 | private: | ||
| 141 | struct TASCommand { | ||
| 142 | u32 buttons{}; | ||
| 143 | TasAnalog l_axis{}; | ||
| 144 | TasAnalog r_axis{}; | ||
| 145 | }; | ||
| 146 | |||
| 147 | // Loads TAS files from all players | ||
| 148 | void LoadTasFiles(); | ||
| 149 | |||
| 150 | // Loads TAS file from the specified player | ||
| 151 | void LoadTasFile(size_t player_index); | ||
| 152 | |||
| 153 | // Writes a TAS file from the recorded commands | ||
| 154 | void WriteTasFile(std::u8string file_name); | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Parses a string containing the axis values with the following format "x;y" | ||
| 158 | * X and Y have a range from -32767 to 32767 | ||
| 159 | * @return Returns a TAS analog object with axis values with range from -1.0 to 1.0 | ||
| 160 | */ | ||
| 161 | TasAnalog ReadCommandAxis(const std::string& line) const; | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Parses a string containing the button values with the following format "a;b;c;d..." | ||
| 165 | * Each button is represented by it's text format specified in text_to_tas_button array | ||
| 166 | * @return Returns a u32 with each bit representing the status of a button | ||
| 167 | */ | ||
| 168 | u32 ReadCommandButtons(const std::string& line) const; | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Converts an u32 containing the button status into the text equivalent | ||
| 172 | * @return Returns a string with the name of the buttons to be written to the file | ||
| 173 | */ | ||
| 174 | std::string WriteCommandButtons(u32 data) const; | ||
| 175 | |||
| 176 | /** | ||
| 177 | * Converts an TAS analog object containing the axis status into the text equivalent | ||
| 178 | * @return Returns a string with the value of the axis to be written to the file | ||
| 179 | */ | ||
| 180 | std::string WriteCommandAxis(TasAnalog data) const; | ||
| 181 | |||
| 182 | // Inverts the Y axis polarity | ||
| 183 | std::pair<float, float> FlipAxisY(std::pair<float, float> old); | ||
| 184 | |||
| 185 | /** | ||
| 186 | * Converts an u32 containing the button status into the text equivalent | ||
| 187 | * @return Returns a string with the name of the buttons to be printed on console | ||
| 188 | */ | ||
| 189 | std::string DebugButtons(u32 buttons) const; | ||
| 190 | |||
| 191 | /** | ||
| 192 | * Converts an TAS analog object containing the axis status into the text equivalent | ||
| 193 | * @return Returns a string with the value of the axis to be printed on console | ||
| 194 | */ | ||
| 195 | std::string DebugJoystick(float x, float y) const; | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Converts the given TAS status into the text equivalent | ||
| 199 | * @return Returns a string with the value of the TAS status to be printed on console | ||
| 200 | */ | ||
| 201 | std::string DebugInput(const TasData& data) const; | ||
| 202 | |||
| 203 | /** | ||
| 204 | * Converts the given TAS status of multiple players into the text equivalent | ||
| 205 | * @return Returns a string with the value of the status of all TAS players to be printed on | ||
| 206 | * console | ||
| 207 | */ | ||
| 208 | std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const; | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Converts an u32 containing the button status into the text equivalent | ||
| 212 | * @return Returns a string with the name of the buttons | ||
| 213 | */ | ||
| 214 | std::string ButtonsToString(u32 button) const; | ||
| 215 | |||
| 216 | // Stores current controller configuration and sets a TAS controller for every active controller | ||
| 217 | // to the current config | ||
| 218 | void SwapToTasController(); | ||
| 219 | |||
| 220 | // Sets the stored controller configuration to the current config | ||
| 221 | void SwapToStoredController(); | ||
| 222 | |||
| 223 | size_t script_length{0}; | ||
| 224 | std::array<TasData, PLAYER_NUMBER> tas_data; | ||
| 225 | bool is_old_input_saved{false}; | ||
| 226 | bool is_recording{false}; | ||
| 227 | bool is_running{false}; | ||
| 228 | bool needs_reset{false}; | ||
| 229 | std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{}; | ||
| 230 | std::vector<TASCommand> record_commands{}; | ||
| 231 | size_t current_command{0}; | ||
| 232 | TASCommand last_input{}; // only used for recording | ||
| 233 | |||
| 234 | // Old settings for swapping controllers | ||
| 235 | std::array<Settings::PlayerInput, 10> player_mappings; | ||
| 236 | }; | ||
| 237 | } // namespace TasInput | ||
diff --git a/src/input_common/tas/tas_poller.cpp b/src/input_common/tas/tas_poller.cpp deleted file mode 100644 index 15810d6b0..000000000 --- a/src/input_common/tas/tas_poller.cpp +++ /dev/null | |||
| @@ -1,101 +0,0 @@ | |||
| 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 <mutex> | ||
| 6 | #include <utility> | ||
| 7 | |||
| 8 | #include "common/settings.h" | ||
| 9 | #include "common/threadsafe_queue.h" | ||
| 10 | #include "input_common/tas/tas_input.h" | ||
| 11 | #include "input_common/tas/tas_poller.h" | ||
| 12 | |||
| 13 | namespace InputCommon { | ||
| 14 | |||
| 15 | class TasButton final : public Input::ButtonDevice { | ||
| 16 | public: | ||
| 17 | explicit TasButton(u32 button_, u32 pad_, const TasInput::Tas* tas_input_) | ||
| 18 | : button(button_), pad(pad_), tas_input(tas_input_) {} | ||
| 19 | |||
| 20 | bool GetStatus() const override { | ||
| 21 | return (tas_input->GetTasState(pad).buttons & button) != 0; | ||
| 22 | } | ||
| 23 | |||
| 24 | private: | ||
| 25 | const u32 button; | ||
| 26 | const u32 pad; | ||
| 27 | const TasInput::Tas* tas_input; | ||
| 28 | }; | ||
| 29 | |||
| 30 | TasButtonFactory::TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_) | ||
| 31 | : tas_input(std::move(tas_input_)) {} | ||
| 32 | |||
| 33 | std::unique_ptr<Input::ButtonDevice> TasButtonFactory::Create(const Common::ParamPackage& params) { | ||
| 34 | const auto button_id = params.Get("button", 0); | ||
| 35 | const auto pad = params.Get("pad", 0); | ||
| 36 | |||
| 37 | return std::make_unique<TasButton>(button_id, pad, tas_input.get()); | ||
| 38 | } | ||
| 39 | |||
| 40 | class TasAnalog final : public Input::AnalogDevice { | ||
| 41 | public: | ||
| 42 | explicit TasAnalog(u32 pad_, u32 axis_x_, u32 axis_y_, const TasInput::Tas* tas_input_) | ||
| 43 | : pad(pad_), axis_x(axis_x_), axis_y(axis_y_), tas_input(tas_input_) {} | ||
| 44 | |||
| 45 | float GetAxis(u32 axis) const { | ||
| 46 | std::lock_guard lock{mutex}; | ||
| 47 | return tas_input->GetTasState(pad).axis.at(axis); | ||
| 48 | } | ||
| 49 | |||
| 50 | std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { | ||
| 51 | float x = GetAxis(analog_axis_x); | ||
| 52 | float y = GetAxis(analog_axis_y); | ||
| 53 | |||
| 54 | // Make sure the coordinates are in the unit circle, | ||
| 55 | // otherwise normalize it. | ||
| 56 | float r = x * x + y * y; | ||
| 57 | if (r > 1.0f) { | ||
| 58 | r = std::sqrt(r); | ||
| 59 | x /= r; | ||
| 60 | y /= r; | ||
| 61 | } | ||
| 62 | |||
| 63 | return {x, y}; | ||
| 64 | } | ||
| 65 | |||
| 66 | std::tuple<float, float> GetStatus() const override { | ||
| 67 | return GetAnalog(axis_x, axis_y); | ||
| 68 | } | ||
| 69 | |||
| 70 | Input::AnalogProperties GetAnalogProperties() const override { | ||
| 71 | return {0.0f, 1.0f, 0.5f}; | ||
| 72 | } | ||
| 73 | |||
| 74 | private: | ||
| 75 | const u32 pad; | ||
| 76 | const u32 axis_x; | ||
| 77 | const u32 axis_y; | ||
| 78 | const TasInput::Tas* tas_input; | ||
| 79 | mutable std::mutex mutex; | ||
| 80 | }; | ||
| 81 | |||
| 82 | /// An analog device factory that creates analog devices from GC Adapter | ||
| 83 | TasAnalogFactory::TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_) | ||
| 84 | : tas_input(std::move(tas_input_)) {} | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Creates analog device from joystick axes | ||
| 88 | * @param params contains parameters for creating the device: | ||
| 89 | * - "port": the nth gcpad on the adapter | ||
| 90 | * - "axis_x": the index of the axis to be bind as x-axis | ||
| 91 | * - "axis_y": the index of the axis to be bind as y-axis | ||
| 92 | */ | ||
| 93 | std::unique_ptr<Input::AnalogDevice> TasAnalogFactory::Create(const Common::ParamPackage& params) { | ||
| 94 | const auto pad = static_cast<u32>(params.Get("pad", 0)); | ||
| 95 | const auto axis_x = static_cast<u32>(params.Get("axis_x", 0)); | ||
| 96 | const auto axis_y = static_cast<u32>(params.Get("axis_y", 1)); | ||
| 97 | |||
| 98 | return std::make_unique<TasAnalog>(pad, axis_x, axis_y, tas_input.get()); | ||
| 99 | } | ||
| 100 | |||
| 101 | } // namespace InputCommon | ||
diff --git a/src/input_common/tas/tas_poller.h b/src/input_common/tas/tas_poller.h deleted file mode 100644 index 09e426cef..000000000 --- a/src/input_common/tas/tas_poller.h +++ /dev/null | |||
| @@ -1,43 +0,0 @@ | |||
| 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 | #include "core/frontend/input.h" | ||
| 9 | #include "input_common/tas/tas_input.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * A button device factory representing a tas bot. It receives tas events and forward them | ||
| 15 | * to all button devices it created. | ||
| 16 | */ | ||
| 17 | class TasButtonFactory final : public Input::Factory<Input::ButtonDevice> { | ||
| 18 | public: | ||
| 19 | explicit TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_); | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Creates a button device from a button press | ||
| 23 | * @param params contains parameters for creating the device: | ||
| 24 | * - "code": the code of the key to bind with the button | ||
| 25 | */ | ||
| 26 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; | ||
| 27 | |||
| 28 | private: | ||
| 29 | std::shared_ptr<TasInput::Tas> tas_input; | ||
| 30 | }; | ||
| 31 | |||
| 32 | /// An analog device factory that creates analog devices from tas | ||
| 33 | class TasAnalogFactory final : public Input::Factory<Input::AnalogDevice> { | ||
| 34 | public: | ||
| 35 | explicit TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_); | ||
| 36 | |||
| 37 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; | ||
| 38 | |||
| 39 | private: | ||
| 40 | std::shared_ptr<TasInput::Tas> tas_input; | ||
| 41 | }; | ||
| 42 | |||
| 43 | } // namespace InputCommon | ||
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp deleted file mode 100644 index 7878a56d7..000000000 --- a/src/input_common/touch_from_button.cpp +++ /dev/null | |||
| @@ -1,53 +0,0 @@ | |||
| 1 | // Copyright 2020 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include "common/settings.h" | ||
| 7 | #include "core/frontend/framebuffer_layout.h" | ||
| 8 | #include "input_common/touch_from_button.h" | ||
| 9 | |||
| 10 | namespace InputCommon { | ||
| 11 | |||
| 12 | class TouchFromButtonDevice final : public Input::TouchDevice { | ||
| 13 | public: | ||
| 14 | TouchFromButtonDevice() { | ||
| 15 | const auto button_index = | ||
| 16 | static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); | ||
| 17 | const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons; | ||
| 18 | |||
| 19 | for (const auto& config_entry : buttons) { | ||
| 20 | const Common::ParamPackage package{config_entry}; | ||
| 21 | map.emplace_back( | ||
| 22 | Input::CreateDevice<Input::ButtonDevice>(config_entry), | ||
| 23 | std::clamp(package.Get("x", 0), 0, static_cast<int>(Layout::ScreenUndocked::Width)), | ||
| 24 | std::clamp(package.Get("y", 0), 0, | ||
| 25 | static_cast<int>(Layout::ScreenUndocked::Height))); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | Input::TouchStatus GetStatus() const override { | ||
| 30 | Input::TouchStatus touch_status{}; | ||
| 31 | for (std::size_t id = 0; id < map.size() && id < touch_status.size(); ++id) { | ||
| 32 | const bool state = std::get<0>(map[id])->GetStatus(); | ||
| 33 | if (state) { | ||
| 34 | const float x = static_cast<float>(std::get<1>(map[id])) / | ||
| 35 | static_cast<int>(Layout::ScreenUndocked::Width); | ||
| 36 | const float y = static_cast<float>(std::get<2>(map[id])) / | ||
| 37 | static_cast<int>(Layout::ScreenUndocked::Height); | ||
| 38 | touch_status[id] = {x, y, true}; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | return touch_status; | ||
| 42 | } | ||
| 43 | |||
| 44 | private: | ||
| 45 | // A vector of the mapped button, its x and its y-coordinate | ||
| 46 | std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map; | ||
| 47 | }; | ||
| 48 | |||
| 49 | std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(const Common::ParamPackage&) { | ||
| 50 | return std::make_unique<TouchFromButtonDevice>(); | ||
| 51 | } | ||
| 52 | |||
| 53 | } // namespace InputCommon | ||
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp deleted file mode 100644 index b9512aa2e..000000000 --- a/src/input_common/udp/client.cpp +++ /dev/null | |||
| @@ -1,526 +0,0 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <chrono> | ||
| 6 | #include <cstring> | ||
| 7 | #include <functional> | ||
| 8 | #include <random> | ||
| 9 | #include <thread> | ||
| 10 | #include <boost/asio.hpp> | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "common/settings.h" | ||
| 13 | #include "input_common/udp/client.h" | ||
| 14 | #include "input_common/udp/protocol.h" | ||
| 15 | |||
| 16 | using boost::asio::ip::udp; | ||
| 17 | |||
| 18 | namespace InputCommon::CemuhookUDP { | ||
| 19 | |||
| 20 | struct SocketCallback { | ||
| 21 | std::function<void(Response::Version)> version; | ||
| 22 | std::function<void(Response::PortInfo)> port_info; | ||
| 23 | std::function<void(Response::PadData)> pad_data; | ||
| 24 | }; | ||
| 25 | |||
| 26 | class Socket { | ||
| 27 | public: | ||
| 28 | using clock = std::chrono::system_clock; | ||
| 29 | |||
| 30 | explicit Socket(const std::string& host, u16 port, SocketCallback callback_) | ||
| 31 | : callback(std::move(callback_)), timer(io_service), | ||
| 32 | socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) { | ||
| 33 | boost::system::error_code ec{}; | ||
| 34 | auto ipv4 = boost::asio::ip::make_address_v4(host, ec); | ||
| 35 | if (ec.value() != boost::system::errc::success) { | ||
| 36 | LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host); | ||
| 37 | ipv4 = boost::asio::ip::address_v4{}; | ||
| 38 | } | ||
| 39 | |||
| 40 | send_endpoint = {udp::endpoint(ipv4, port)}; | ||
| 41 | } | ||
| 42 | |||
| 43 | void Stop() { | ||
| 44 | io_service.stop(); | ||
| 45 | } | ||
| 46 | |||
| 47 | void Loop() { | ||
| 48 | io_service.run(); | ||
| 49 | } | ||
| 50 | |||
| 51 | void StartSend(const clock::time_point& from) { | ||
| 52 | timer.expires_at(from + std::chrono::seconds(3)); | ||
| 53 | timer.async_wait([this](const boost::system::error_code& error) { HandleSend(error); }); | ||
| 54 | } | ||
| 55 | |||
| 56 | void StartReceive() { | ||
| 57 | socket.async_receive_from( | ||
| 58 | boost::asio::buffer(receive_buffer), receive_endpoint, | ||
| 59 | [this](const boost::system::error_code& error, std::size_t bytes_transferred) { | ||
| 60 | HandleReceive(error, bytes_transferred); | ||
| 61 | }); | ||
| 62 | } | ||
| 63 | |||
| 64 | private: | ||
| 65 | u32 GenerateRandomClientId() const { | ||
| 66 | std::random_device device; | ||
| 67 | return device(); | ||
| 68 | } | ||
| 69 | |||
| 70 | void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) { | ||
| 71 | if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { | ||
| 72 | switch (*type) { | ||
| 73 | case Type::Version: { | ||
| 74 | Response::Version version; | ||
| 75 | std::memcpy(&version, &receive_buffer[sizeof(Header)], sizeof(Response::Version)); | ||
| 76 | callback.version(std::move(version)); | ||
| 77 | break; | ||
| 78 | } | ||
| 79 | case Type::PortInfo: { | ||
| 80 | Response::PortInfo port_info; | ||
| 81 | std::memcpy(&port_info, &receive_buffer[sizeof(Header)], | ||
| 82 | sizeof(Response::PortInfo)); | ||
| 83 | callback.port_info(std::move(port_info)); | ||
| 84 | break; | ||
| 85 | } | ||
| 86 | case Type::PadData: { | ||
| 87 | Response::PadData pad_data; | ||
| 88 | std::memcpy(&pad_data, &receive_buffer[sizeof(Header)], sizeof(Response::PadData)); | ||
| 89 | SanitizeMotion(pad_data); | ||
| 90 | callback.pad_data(std::move(pad_data)); | ||
| 91 | break; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | StartReceive(); | ||
| 96 | } | ||
| 97 | |||
| 98 | void HandleSend(const boost::system::error_code&) { | ||
| 99 | boost::system::error_code _ignored{}; | ||
| 100 | // Send a request for getting port info for the pad | ||
| 101 | const Request::PortInfo port_info{4, {0, 1, 2, 3}}; | ||
| 102 | const auto port_message = Request::Create(port_info, client_id); | ||
| 103 | std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); | ||
| 104 | socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored); | ||
| 105 | |||
| 106 | // Send a request for getting pad data for the pad | ||
| 107 | const Request::PadData pad_data{ | ||
| 108 | Request::PadData::Flags::AllPorts, | ||
| 109 | 0, | ||
| 110 | EMPTY_MAC_ADDRESS, | ||
| 111 | }; | ||
| 112 | const auto pad_message = Request::Create(pad_data, client_id); | ||
| 113 | std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE); | ||
| 114 | socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored); | ||
| 115 | StartSend(timer.expiry()); | ||
| 116 | } | ||
| 117 | |||
| 118 | void SanitizeMotion(Response::PadData& data) { | ||
| 119 | // Zero out any non number value | ||
| 120 | if (!std::isnormal(data.gyro.pitch)) { | ||
| 121 | data.gyro.pitch = 0; | ||
| 122 | } | ||
| 123 | if (!std::isnormal(data.gyro.roll)) { | ||
| 124 | data.gyro.roll = 0; | ||
| 125 | } | ||
| 126 | if (!std::isnormal(data.gyro.yaw)) { | ||
| 127 | data.gyro.yaw = 0; | ||
| 128 | } | ||
| 129 | if (!std::isnormal(data.accel.x)) { | ||
| 130 | data.accel.x = 0; | ||
| 131 | } | ||
| 132 | if (!std::isnormal(data.accel.y)) { | ||
| 133 | data.accel.y = 0; | ||
| 134 | } | ||
| 135 | if (!std::isnormal(data.accel.z)) { | ||
| 136 | data.accel.z = 0; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | SocketCallback callback; | ||
| 141 | boost::asio::io_service io_service; | ||
| 142 | boost::asio::basic_waitable_timer<clock> timer; | ||
| 143 | udp::socket socket; | ||
| 144 | |||
| 145 | const u32 client_id; | ||
| 146 | |||
| 147 | static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); | ||
| 148 | static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>); | ||
| 149 | std::array<u8, PORT_INFO_SIZE> send_buffer1; | ||
| 150 | std::array<u8, PAD_DATA_SIZE> send_buffer2; | ||
| 151 | udp::endpoint send_endpoint; | ||
| 152 | |||
| 153 | std::array<u8, MAX_PACKET_SIZE> receive_buffer; | ||
| 154 | udp::endpoint receive_endpoint; | ||
| 155 | }; | ||
| 156 | |||
| 157 | static void SocketLoop(Socket* socket) { | ||
| 158 | socket->StartReceive(); | ||
| 159 | socket->StartSend(Socket::clock::now()); | ||
| 160 | socket->Loop(); | ||
| 161 | } | ||
| 162 | |||
| 163 | Client::Client() { | ||
| 164 | LOG_INFO(Input, "Udp Initialization started"); | ||
| 165 | finger_id.fill(MAX_TOUCH_FINGERS); | ||
| 166 | ReloadSockets(); | ||
| 167 | } | ||
| 168 | |||
| 169 | Client::~Client() { | ||
| 170 | Reset(); | ||
| 171 | } | ||
| 172 | |||
| 173 | Client::ClientConnection::ClientConnection() = default; | ||
| 174 | |||
| 175 | Client::ClientConnection::~ClientConnection() = default; | ||
| 176 | |||
| 177 | std::vector<Common::ParamPackage> Client::GetInputDevices() const { | ||
| 178 | std::vector<Common::ParamPackage> devices; | ||
| 179 | for (std::size_t pad = 0; pad < pads.size(); pad++) { | ||
| 180 | if (!DeviceConnected(pad)) { | ||
| 181 | continue; | ||
| 182 | } | ||
| 183 | std::string name = fmt::format("UDP Controller {}", pad); | ||
| 184 | devices.emplace_back(Common::ParamPackage{ | ||
| 185 | {"class", "cemuhookudp"}, | ||
| 186 | {"display", std::move(name)}, | ||
| 187 | {"port", std::to_string(pad)}, | ||
| 188 | }); | ||
| 189 | } | ||
| 190 | return devices; | ||
| 191 | } | ||
| 192 | |||
| 193 | bool Client::DeviceConnected(std::size_t pad) const { | ||
| 194 | // Use last timestamp to detect if the socket has stopped sending data | ||
| 195 | const auto now = std::chrono::steady_clock::now(); | ||
| 196 | const auto time_difference = static_cast<u64>( | ||
| 197 | std::chrono::duration_cast<std::chrono::milliseconds>(now - pads[pad].last_update).count()); | ||
| 198 | return time_difference < 1000 && pads[pad].connected; | ||
| 199 | } | ||
| 200 | |||
| 201 | void Client::ReloadSockets() { | ||
| 202 | Reset(); | ||
| 203 | |||
| 204 | std::stringstream servers_ss(static_cast<std::string>(Settings::values.udp_input_servers)); | ||
| 205 | std::string server_token; | ||
| 206 | std::size_t client = 0; | ||
| 207 | while (std::getline(servers_ss, server_token, ',')) { | ||
| 208 | if (client == MAX_UDP_CLIENTS) { | ||
| 209 | break; | ||
| 210 | } | ||
| 211 | std::stringstream server_ss(server_token); | ||
| 212 | std::string token; | ||
| 213 | std::getline(server_ss, token, ':'); | ||
| 214 | std::string udp_input_address = token; | ||
| 215 | std::getline(server_ss, token, ':'); | ||
| 216 | char* temp; | ||
| 217 | const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0)); | ||
| 218 | if (*temp != '\0') { | ||
| 219 | LOG_ERROR(Input, "Port number is not valid {}", token); | ||
| 220 | continue; | ||
| 221 | } | ||
| 222 | |||
| 223 | const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port); | ||
| 224 | if (client_number != MAX_UDP_CLIENTS) { | ||
| 225 | LOG_ERROR(Input, "Duplicated UDP servers found"); | ||
| 226 | continue; | ||
| 227 | } | ||
| 228 | StartCommunication(client++, udp_input_address, udp_input_port); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | std::size_t Client::GetClientNumber(std::string_view host, u16 port) const { | ||
| 233 | for (std::size_t client = 0; client < clients.size(); client++) { | ||
| 234 | if (clients[client].active == -1) { | ||
| 235 | continue; | ||
| 236 | } | ||
| 237 | if (clients[client].host == host && clients[client].port == port) { | ||
| 238 | return client; | ||
| 239 | } | ||
| 240 | } | ||
| 241 | return MAX_UDP_CLIENTS; | ||
| 242 | } | ||
| 243 | |||
| 244 | void Client::OnVersion([[maybe_unused]] Response::Version data) { | ||
| 245 | LOG_TRACE(Input, "Version packet received: {}", data.version); | ||
| 246 | } | ||
| 247 | |||
| 248 | void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) { | ||
| 249 | LOG_TRACE(Input, "PortInfo packet received: {}", data.model); | ||
| 250 | } | ||
| 251 | |||
| 252 | void Client::OnPadData(Response::PadData data, std::size_t client) { | ||
| 253 | const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id; | ||
| 254 | |||
| 255 | if (pad_index >= pads.size()) { | ||
| 256 | LOG_ERROR(Input, "Invalid pad id {}", data.info.id); | ||
| 257 | return; | ||
| 258 | } | ||
| 259 | |||
| 260 | LOG_TRACE(Input, "PadData packet received"); | ||
| 261 | if (data.packet_counter == pads[pad_index].packet_sequence) { | ||
| 262 | LOG_WARNING( | ||
| 263 | Input, | ||
| 264 | "PadData packet dropped because its stale info. Current count: {} Packet count: {}", | ||
| 265 | pads[pad_index].packet_sequence, data.packet_counter); | ||
| 266 | pads[pad_index].connected = false; | ||
| 267 | return; | ||
| 268 | } | ||
| 269 | |||
| 270 | clients[client].active = 1; | ||
| 271 | pads[pad_index].connected = true; | ||
| 272 | pads[pad_index].packet_sequence = data.packet_counter; | ||
| 273 | |||
| 274 | const auto now = std::chrono::steady_clock::now(); | ||
| 275 | const auto time_difference = static_cast<u64>( | ||
| 276 | std::chrono::duration_cast<std::chrono::microseconds>(now - pads[pad_index].last_update) | ||
| 277 | .count()); | ||
| 278 | pads[pad_index].last_update = now; | ||
| 279 | |||
| 280 | const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; | ||
| 281 | pads[pad_index].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); | ||
| 282 | // Gyroscope values are not it the correct scale from better joy. | ||
| 283 | // Dividing by 312 allows us to make one full turn = 1 turn | ||
| 284 | // This must be a configurable valued called sensitivity | ||
| 285 | pads[pad_index].motion.SetGyroscope(raw_gyroscope / 312.0f); | ||
| 286 | pads[pad_index].motion.UpdateRotation(time_difference); | ||
| 287 | pads[pad_index].motion.UpdateOrientation(time_difference); | ||
| 288 | |||
| 289 | { | ||
| 290 | std::lock_guard guard(pads[pad_index].status.update_mutex); | ||
| 291 | pads[pad_index].status.motion_status = pads[pad_index].motion.GetMotion(); | ||
| 292 | |||
| 293 | for (std::size_t id = 0; id < data.touch.size(); ++id) { | ||
| 294 | UpdateTouchInput(data.touch[id], client, id); | ||
| 295 | } | ||
| 296 | |||
| 297 | if (configuring) { | ||
| 298 | const Common::Vec3f gyroscope = pads[pad_index].motion.GetGyroscope(); | ||
| 299 | const Common::Vec3f accelerometer = pads[pad_index].motion.GetAcceleration(); | ||
| 300 | UpdateYuzuSettings(client, data.info.id, accelerometer, gyroscope); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | void Client::StartCommunication(std::size_t client, const std::string& host, u16 port) { | ||
| 306 | SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, | ||
| 307 | [this](Response::PortInfo info) { OnPortInfo(info); }, | ||
| 308 | [this, client](Response::PadData data) { OnPadData(data, client); }}; | ||
| 309 | LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); | ||
| 310 | clients[client].host = host; | ||
| 311 | clients[client].port = port; | ||
| 312 | clients[client].active = 0; | ||
| 313 | clients[client].socket = std::make_unique<Socket>(host, port, callback); | ||
| 314 | clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; | ||
| 315 | |||
| 316 | // Set motion parameters | ||
| 317 | // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode | ||
| 318 | // Real HW values are unknown, 0.0001 is an approximate to Standard | ||
| 319 | for (std::size_t pad = 0; pad < PADS_PER_CLIENT; pad++) { | ||
| 320 | pads[client * PADS_PER_CLIENT + pad].motion.SetGyroThreshold(0.0001f); | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | void Client::Reset() { | ||
| 325 | for (auto& client : clients) { | ||
| 326 | if (client.thread.joinable()) { | ||
| 327 | client.active = -1; | ||
| 328 | client.socket->Stop(); | ||
| 329 | client.thread.join(); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index, | ||
| 335 | const Common::Vec3<float>& acc, const Common::Vec3<float>& gyro) { | ||
| 336 | if (gyro.Length() > 0.2f) { | ||
| 337 | LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client, | ||
| 338 | gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]); | ||
| 339 | } | ||
| 340 | UDPPadStatus pad{ | ||
| 341 | .host = clients[client].host, | ||
| 342 | .port = clients[client].port, | ||
| 343 | .pad_index = pad_index, | ||
| 344 | }; | ||
| 345 | for (std::size_t i = 0; i < 3; ++i) { | ||
| 346 | if (gyro[i] > 5.0f || gyro[i] < -5.0f) { | ||
| 347 | pad.motion = static_cast<PadMotion>(i); | ||
| 348 | pad.motion_value = gyro[i]; | ||
| 349 | pad_queue.Push(pad); | ||
| 350 | } | ||
| 351 | if (acc[i] > 1.75f || acc[i] < -1.75f) { | ||
| 352 | pad.motion = static_cast<PadMotion>(i + 3); | ||
| 353 | pad.motion_value = acc[i]; | ||
| 354 | pad_queue.Push(pad); | ||
| 355 | } | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | std::optional<std::size_t> Client::GetUnusedFingerID() const { | ||
| 360 | std::size_t first_free_id = 0; | ||
| 361 | while (first_free_id < MAX_TOUCH_FINGERS) { | ||
| 362 | if (!std::get<2>(touch_status[first_free_id])) { | ||
| 363 | return first_free_id; | ||
| 364 | } else { | ||
| 365 | first_free_id++; | ||
| 366 | } | ||
| 367 | } | ||
| 368 | return std::nullopt; | ||
| 369 | } | ||
| 370 | |||
| 371 | void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) { | ||
| 372 | // TODO: Use custom calibration per device | ||
| 373 | const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); | ||
| 374 | const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100)); | ||
| 375 | const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50)); | ||
| 376 | const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800)); | ||
| 377 | const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850)); | ||
| 378 | const std::size_t touch_id = client * 2 + id; | ||
| 379 | if (touch_pad.is_active) { | ||
| 380 | if (finger_id[touch_id] == MAX_TOUCH_FINGERS) { | ||
| 381 | const auto first_free_id = GetUnusedFingerID(); | ||
| 382 | if (!first_free_id) { | ||
| 383 | // Invalid finger id skip to next input | ||
| 384 | return; | ||
| 385 | } | ||
| 386 | finger_id[touch_id] = *first_free_id; | ||
| 387 | } | ||
| 388 | auto& [x, y, pressed] = touch_status[finger_id[touch_id]]; | ||
| 389 | x = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) / | ||
| 390 | static_cast<float>(max_x - min_x); | ||
| 391 | y = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) / | ||
| 392 | static_cast<float>(max_y - min_y); | ||
| 393 | pressed = true; | ||
| 394 | return; | ||
| 395 | } | ||
| 396 | |||
| 397 | if (finger_id[touch_id] != MAX_TOUCH_FINGERS) { | ||
| 398 | touch_status[finger_id[touch_id]] = {}; | ||
| 399 | finger_id[touch_id] = MAX_TOUCH_FINGERS; | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | void Client::BeginConfiguration() { | ||
| 404 | pad_queue.Clear(); | ||
| 405 | configuring = true; | ||
| 406 | } | ||
| 407 | |||
| 408 | void Client::EndConfiguration() { | ||
| 409 | pad_queue.Clear(); | ||
| 410 | configuring = false; | ||
| 411 | } | ||
| 412 | |||
| 413 | DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) { | ||
| 414 | const std::size_t client_number = GetClientNumber(host, port); | ||
| 415 | if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) { | ||
| 416 | return pads[0].status; | ||
| 417 | } | ||
| 418 | return pads[(client_number * PADS_PER_CLIENT) + pad].status; | ||
| 419 | } | ||
| 420 | |||
| 421 | const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const { | ||
| 422 | const std::size_t client_number = GetClientNumber(host, port); | ||
| 423 | if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) { | ||
| 424 | return pads[0].status; | ||
| 425 | } | ||
| 426 | return pads[(client_number * PADS_PER_CLIENT) + pad].status; | ||
| 427 | } | ||
| 428 | |||
| 429 | Input::TouchStatus& Client::GetTouchState() { | ||
| 430 | return touch_status; | ||
| 431 | } | ||
| 432 | |||
| 433 | const Input::TouchStatus& Client::GetTouchState() const { | ||
| 434 | return touch_status; | ||
| 435 | } | ||
| 436 | |||
| 437 | Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() { | ||
| 438 | return pad_queue; | ||
| 439 | } | ||
| 440 | |||
| 441 | const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const { | ||
| 442 | return pad_queue; | ||
| 443 | } | ||
| 444 | |||
| 445 | void TestCommunication(const std::string& host, u16 port, | ||
| 446 | const std::function<void()>& success_callback, | ||
| 447 | const std::function<void()>& failure_callback) { | ||
| 448 | std::thread([=] { | ||
| 449 | Common::Event success_event; | ||
| 450 | SocketCallback callback{ | ||
| 451 | .version = [](Response::Version) {}, | ||
| 452 | .port_info = [](Response::PortInfo) {}, | ||
| 453 | .pad_data = [&](Response::PadData) { success_event.Set(); }, | ||
| 454 | }; | ||
| 455 | Socket socket{host, port, std::move(callback)}; | ||
| 456 | std::thread worker_thread{SocketLoop, &socket}; | ||
| 457 | const bool result = | ||
| 458 | success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10)); | ||
| 459 | socket.Stop(); | ||
| 460 | worker_thread.join(); | ||
| 461 | if (result) { | ||
| 462 | success_callback(); | ||
| 463 | } else { | ||
| 464 | failure_callback(); | ||
| 465 | } | ||
| 466 | }).detach(); | ||
| 467 | } | ||
| 468 | |||
| 469 | CalibrationConfigurationJob::CalibrationConfigurationJob( | ||
| 470 | const std::string& host, u16 port, std::function<void(Status)> status_callback, | ||
| 471 | std::function<void(u16, u16, u16, u16)> data_callback) { | ||
| 472 | |||
| 473 | std::thread([=, this] { | ||
| 474 | Status current_status{Status::Initialized}; | ||
| 475 | SocketCallback callback{ | ||
| 476 | [](Response::Version) {}, [](Response::PortInfo) {}, | ||
| 477 | [&](Response::PadData data) { | ||
| 478 | static constexpr u16 CALIBRATION_THRESHOLD = 100; | ||
| 479 | static constexpr u16 MAX_VALUE = UINT16_MAX; | ||
| 480 | |||
| 481 | if (current_status == Status::Initialized) { | ||
| 482 | // Receiving data means the communication is ready now | ||
| 483 | current_status = Status::Ready; | ||
| 484 | status_callback(current_status); | ||
| 485 | } | ||
| 486 | const auto& touchpad_0 = data.touch[0]; | ||
| 487 | if (touchpad_0.is_active == 0) { | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y); | ||
| 491 | const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x)); | ||
| 492 | const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y)); | ||
| 493 | if (current_status == Status::Ready) { | ||
| 494 | // First touch - min data (min_x/min_y) | ||
| 495 | current_status = Status::Stage1Completed; | ||
| 496 | status_callback(current_status); | ||
| 497 | } | ||
| 498 | if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD && | ||
| 499 | touchpad_0.y - min_y > CALIBRATION_THRESHOLD) { | ||
| 500 | // Set the current position as max value and finishes configuration | ||
| 501 | const u16 max_x = touchpad_0.x; | ||
| 502 | const u16 max_y = touchpad_0.y; | ||
| 503 | current_status = Status::Completed; | ||
| 504 | data_callback(min_x, min_y, max_x, max_y); | ||
| 505 | status_callback(current_status); | ||
| 506 | |||
| 507 | complete_event.Set(); | ||
| 508 | } | ||
| 509 | }}; | ||
| 510 | Socket socket{host, port, std::move(callback)}; | ||
| 511 | std::thread worker_thread{SocketLoop, &socket}; | ||
| 512 | complete_event.Wait(); | ||
| 513 | socket.Stop(); | ||
| 514 | worker_thread.join(); | ||
| 515 | }).detach(); | ||
| 516 | } | ||
| 517 | |||
| 518 | CalibrationConfigurationJob::~CalibrationConfigurationJob() { | ||
| 519 | Stop(); | ||
| 520 | } | ||
| 521 | |||
| 522 | void CalibrationConfigurationJob::Stop() { | ||
| 523 | complete_event.Set(); | ||
| 524 | } | ||
| 525 | |||
| 526 | } // namespace InputCommon::CemuhookUDP | ||
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp deleted file mode 100644 index 9829da6f0..000000000 --- a/src/input_common/udp/udp.cpp +++ /dev/null | |||
| @@ -1,110 +0,0 @@ | |||
| 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 <mutex> | ||
| 6 | #include <utility> | ||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/threadsafe_queue.h" | ||
| 9 | #include "input_common/udp/client.h" | ||
| 10 | #include "input_common/udp/udp.h" | ||
| 11 | |||
| 12 | namespace InputCommon { | ||
| 13 | |||
| 14 | class UDPMotion final : public Input::MotionDevice { | ||
| 15 | public: | ||
| 16 | explicit UDPMotion(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_) | ||
| 17 | : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} | ||
| 18 | |||
| 19 | Input::MotionStatus GetStatus() const override { | ||
| 20 | return client->GetPadState(ip, port, pad).motion_status; | ||
| 21 | } | ||
| 22 | |||
| 23 | private: | ||
| 24 | const std::string ip; | ||
| 25 | const u16 port; | ||
| 26 | const u16 pad; | ||
| 27 | CemuhookUDP::Client* client; | ||
| 28 | mutable std::mutex mutex; | ||
| 29 | }; | ||
| 30 | |||
| 31 | /// A motion device factory that creates motion devices from a UDP client | ||
| 32 | UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) | ||
| 33 | : client(std::move(client_)) {} | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Creates motion device | ||
| 37 | * @param params contains parameters for creating the device: | ||
| 38 | * - "port": the UDP port number | ||
| 39 | */ | ||
| 40 | std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { | ||
| 41 | auto ip = params.Get("ip", "127.0.0.1"); | ||
| 42 | const auto port = static_cast<u16>(params.Get("port", 26760)); | ||
| 43 | const auto pad = static_cast<u16>(params.Get("pad_index", 0)); | ||
| 44 | |||
| 45 | return std::make_unique<UDPMotion>(std::move(ip), port, pad, client.get()); | ||
| 46 | } | ||
| 47 | |||
| 48 | void UDPMotionFactory::BeginConfiguration() { | ||
| 49 | polling = true; | ||
| 50 | client->BeginConfiguration(); | ||
| 51 | } | ||
| 52 | |||
| 53 | void UDPMotionFactory::EndConfiguration() { | ||
| 54 | polling = false; | ||
| 55 | client->EndConfiguration(); | ||
| 56 | } | ||
| 57 | |||
| 58 | Common::ParamPackage UDPMotionFactory::GetNextInput() { | ||
| 59 | Common::ParamPackage params; | ||
| 60 | CemuhookUDP::UDPPadStatus pad; | ||
| 61 | auto& queue = client->GetPadQueue(); | ||
| 62 | while (queue.Pop(pad)) { | ||
| 63 | if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) { | ||
| 64 | continue; | ||
| 65 | } | ||
| 66 | params.Set("engine", "cemuhookudp"); | ||
| 67 | params.Set("ip", pad.host); | ||
| 68 | params.Set("port", static_cast<u16>(pad.port)); | ||
| 69 | params.Set("pad_index", static_cast<u16>(pad.pad_index)); | ||
| 70 | params.Set("motion", static_cast<u16>(pad.motion)); | ||
| 71 | return params; | ||
| 72 | } | ||
| 73 | return params; | ||
| 74 | } | ||
| 75 | |||
| 76 | class UDPTouch final : public Input::TouchDevice { | ||
| 77 | public: | ||
| 78 | explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_) | ||
| 79 | : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} | ||
| 80 | |||
| 81 | Input::TouchStatus GetStatus() const override { | ||
| 82 | return client->GetTouchState(); | ||
| 83 | } | ||
| 84 | |||
| 85 | private: | ||
| 86 | const std::string ip; | ||
| 87 | [[maybe_unused]] const u16 port; | ||
| 88 | [[maybe_unused]] const u16 pad; | ||
| 89 | CemuhookUDP::Client* client; | ||
| 90 | mutable std::mutex mutex; | ||
| 91 | }; | ||
| 92 | |||
| 93 | /// A motion device factory that creates motion devices from a UDP client | ||
| 94 | UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) | ||
| 95 | : client(std::move(client_)) {} | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Creates motion device | ||
| 99 | * @param params contains parameters for creating the device: | ||
| 100 | * - "port": the UDP port number | ||
| 101 | */ | ||
| 102 | std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { | ||
| 103 | auto ip = params.Get("ip", "127.0.0.1"); | ||
| 104 | const auto port = static_cast<u16>(params.Get("port", 26760)); | ||
| 105 | const auto pad = static_cast<u16>(params.Get("pad_index", 0)); | ||
| 106 | |||
| 107 | return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get()); | ||
| 108 | } | ||
| 109 | |||
| 110 | } // namespace InputCommon | ||
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h deleted file mode 100644 index ea3fd4175..000000000 --- a/src/input_common/udp/udp.h +++ /dev/null | |||
| @@ -1,57 +0,0 @@ | |||
| 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 <memory> | ||
| 8 | #include "core/frontend/input.h" | ||
| 9 | #include "input_common/udp/client.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | /// A motion device factory that creates motion devices from udp clients | ||
| 14 | class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> { | ||
| 15 | public: | ||
| 16 | explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_); | ||
| 17 | |||
| 18 | std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override; | ||
| 19 | |||
| 20 | Common::ParamPackage GetNextInput(); | ||
| 21 | |||
| 22 | /// For device input configuration/polling | ||
| 23 | void BeginConfiguration(); | ||
| 24 | void EndConfiguration(); | ||
| 25 | |||
| 26 | bool IsPolling() const { | ||
| 27 | return polling; | ||
| 28 | } | ||
| 29 | |||
| 30 | private: | ||
| 31 | std::shared_ptr<CemuhookUDP::Client> client; | ||
| 32 | bool polling = false; | ||
| 33 | }; | ||
| 34 | |||
| 35 | /// A touch device factory that creates touch devices from udp clients | ||
| 36 | class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { | ||
| 37 | public: | ||
| 38 | explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_); | ||
| 39 | |||
| 40 | std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override; | ||
| 41 | |||
| 42 | Common::ParamPackage GetNextInput(); | ||
| 43 | |||
| 44 | /// For device input configuration/polling | ||
| 45 | void BeginConfiguration(); | ||
| 46 | void EndConfiguration(); | ||
| 47 | |||
| 48 | bool IsPolling() const { | ||
| 49 | return polling; | ||
| 50 | } | ||
| 51 | |||
| 52 | private: | ||
| 53 | std::shared_ptr<CemuhookUDP::Client> client; | ||
| 54 | bool polling = false; | ||
| 55 | }; | ||
| 56 | |||
| 57 | } // namespace InputCommon | ||
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index bc3df80c8..4c76ce1ea 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | add_library(shader_recompiler STATIC | 1 | add_library(shader_recompiler STATIC |
| 2 | backend/bindings.h | 2 | backend/bindings.h |
| 3 | backend/glasm/emit_context.cpp | ||
| 4 | backend/glasm/emit_context.h | ||
| 5 | backend/glasm/emit_glasm.cpp | 3 | backend/glasm/emit_glasm.cpp |
| 6 | backend/glasm/emit_glasm.h | 4 | backend/glasm/emit_glasm.h |
| 7 | backend/glasm/emit_glasm_barriers.cpp | 5 | backend/glasm/emit_glasm_barriers.cpp |
| @@ -22,10 +20,10 @@ add_library(shader_recompiler STATIC | |||
| 22 | backend/glasm/emit_glasm_special.cpp | 20 | backend/glasm/emit_glasm_special.cpp |
| 23 | backend/glasm/emit_glasm_undefined.cpp | 21 | backend/glasm/emit_glasm_undefined.cpp |
| 24 | backend/glasm/emit_glasm_warp.cpp | 22 | backend/glasm/emit_glasm_warp.cpp |
| 23 | backend/glasm/glasm_emit_context.cpp | ||
| 24 | backend/glasm/glasm_emit_context.h | ||
| 25 | backend/glasm/reg_alloc.cpp | 25 | backend/glasm/reg_alloc.cpp |
| 26 | backend/glasm/reg_alloc.h | 26 | backend/glasm/reg_alloc.h |
| 27 | backend/glsl/emit_context.cpp | ||
| 28 | backend/glsl/emit_context.h | ||
| 29 | backend/glsl/emit_glsl.cpp | 27 | backend/glsl/emit_glsl.cpp |
| 30 | backend/glsl/emit_glsl.h | 28 | backend/glsl/emit_glsl.h |
| 31 | backend/glsl/emit_glsl_atomic.cpp | 29 | backend/glsl/emit_glsl_atomic.cpp |
| @@ -47,10 +45,10 @@ add_library(shader_recompiler STATIC | |||
| 47 | backend/glsl/emit_glsl_special.cpp | 45 | backend/glsl/emit_glsl_special.cpp |
| 48 | backend/glsl/emit_glsl_undefined.cpp | 46 | backend/glsl/emit_glsl_undefined.cpp |
| 49 | backend/glsl/emit_glsl_warp.cpp | 47 | backend/glsl/emit_glsl_warp.cpp |
| 48 | backend/glsl/glsl_emit_context.cpp | ||
| 49 | backend/glsl/glsl_emit_context.h | ||
| 50 | backend/glsl/var_alloc.cpp | 50 | backend/glsl/var_alloc.cpp |
| 51 | backend/glsl/var_alloc.h | 51 | backend/glsl/var_alloc.h |
| 52 | backend/spirv/emit_context.cpp | ||
| 53 | backend/spirv/emit_context.h | ||
| 54 | backend/spirv/emit_spirv.cpp | 52 | backend/spirv/emit_spirv.cpp |
| 55 | backend/spirv/emit_spirv.h | 53 | backend/spirv/emit_spirv.h |
| 56 | backend/spirv/emit_spirv_atomic.cpp | 54 | backend/spirv/emit_spirv_atomic.cpp |
| @@ -72,6 +70,8 @@ add_library(shader_recompiler STATIC | |||
| 72 | backend/spirv/emit_spirv_special.cpp | 70 | backend/spirv/emit_spirv_special.cpp |
| 73 | backend/spirv/emit_spirv_undefined.cpp | 71 | backend/spirv/emit_spirv_undefined.cpp |
| 74 | backend/spirv/emit_spirv_warp.cpp | 72 | backend/spirv/emit_spirv_warp.cpp |
| 73 | backend/spirv/spirv_emit_context.cpp | ||
| 74 | backend/spirv/spirv_emit_context.h | ||
| 75 | environment.h | 75 | environment.h |
| 76 | exception.h | 76 | exception.h |
| 77 | frontend/ir/abstract_syntax_list.h | 77 | frontend/ir/abstract_syntax_list.h |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index 004658546..42eff443f 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp | |||
| @@ -9,9 +9,9 @@ | |||
| 9 | #include "common/div_ceil.h" | 9 | #include "common/div_ceil.h" |
| 10 | #include "common/settings.h" | 10 | #include "common/settings.h" |
| 11 | #include "shader_recompiler/backend/bindings.h" | 11 | #include "shader_recompiler/backend/bindings.h" |
| 12 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 13 | #include "shader_recompiler/backend/glasm/emit_glasm.h" | 12 | #include "shader_recompiler/backend/glasm/emit_glasm.h" |
| 14 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 13 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 14 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 15 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 15 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 16 | #include "shader_recompiler/frontend/ir/program.h" | 16 | #include "shader_recompiler/frontend/ir/program.h" |
| 17 | #include "shader_recompiler/profile.h" | 17 | #include "shader_recompiler/profile.h" |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp index e69de29bb..c0b97683e 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp | |||
| @@ -0,0 +1,22 @@ | |||
| 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 "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::GLASM { | ||
| 9 | |||
| 10 | void EmitBarrier(EmitContext& ctx) { | ||
| 11 | ctx.Add("BAR;"); | ||
| 12 | } | ||
| 13 | |||
| 14 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx) { | ||
| 15 | ctx.Add("MEMBAR.CTA;"); | ||
| 16 | } | ||
| 17 | |||
| 18 | void EmitDeviceMemoryBarrier(EmitContext& ctx) { | ||
| 19 | ctx.Add("MEMBAR;"); | ||
| 20 | } | ||
| 21 | |||
| 22 | } // namespace Shader::Backend::GLASM | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp index 9201ccd39..3bfcbbe65 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 5 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/value.h" | 7 | #include "shader_recompiler/frontend/ir/value.h" |
| 8 | 8 | ||
| 9 | namespace Shader::Backend::GLASM { | 9 | namespace Shader::Backend::GLASM { |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp index bff0b7c1c..babbe6654 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 5 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/value.h" | 7 | #include "shader_recompiler/frontend/ir/value.h" |
| 8 | 8 | ||
| 9 | namespace Shader::Backend::GLASM { | 9 | namespace Shader::Backend::GLASM { |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index 02c9dc6d7..081b2c8e0 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 7 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 8 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | #include "shader_recompiler/profile.h" | 10 | #include "shader_recompiler/profile.h" |
| 11 | #include "shader_recompiler/shader_info.h" | 11 | #include "shader_recompiler/shader_info.h" |
| @@ -335,6 +335,35 @@ void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value) { | |||
| 335 | ctx.Add("MOV.F result.depth.z,{};", value); | 335 | ctx.Add("MOV.F result.depth.z,{};", value); |
| 336 | } | 336 | } |
| 337 | 337 | ||
| 338 | void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) { | ||
| 339 | ctx.Add("MOV.S {},invocation.groupid;", inst); | ||
| 340 | } | ||
| 341 | |||
| 342 | void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) { | ||
| 343 | ctx.Add("MOV.S {},invocation.localid;", inst); | ||
| 344 | } | ||
| 345 | |||
| 346 | void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) { | ||
| 347 | ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst); | ||
| 348 | } | ||
| 349 | |||
| 350 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { | ||
| 351 | ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst); | ||
| 352 | } | ||
| 353 | |||
| 354 | void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) { | ||
| 355 | ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst); | ||
| 356 | } | ||
| 357 | |||
| 358 | void EmitYDirection(EmitContext& ctx, IR::Inst& inst) { | ||
| 359 | ctx.uses_y_direction = true; | ||
| 360 | ctx.Add("MOV.F {}.x,y_direction[0].w;", inst); | ||
| 361 | } | ||
| 362 | |||
| 363 | void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { | ||
| 364 | ctx.Add("MOV.F {}.x,scaling[0].z;", inst); | ||
| 365 | } | ||
| 366 | |||
| 338 | void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { | 367 | void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { |
| 339 | ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); | 368 | ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); |
| 340 | } | 369 | } |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp index e69de29bb..8a14fc8d9 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 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 "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::GLASM { | ||
| 9 | |||
| 10 | void EmitJoin(EmitContext&) { | ||
| 11 | throw NotImplementedException("Join shouldn't be emitted"); | ||
| 12 | } | ||
| 13 | |||
| 14 | void EmitDemoteToHelperInvocation(EmitContext& ctx) { | ||
| 15 | ctx.Add("KIL TR.x;"); | ||
| 16 | } | ||
| 17 | |||
| 18 | } // namespace Shader::Backend::GLASM | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp index ccdf1cbc8..4cff70fe4 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 7 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 8 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/modifiers.h" | 9 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 10 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 11 | 11 | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp index 4ed58619d..356640471 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 7 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 8 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/modifiers.h" | 9 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 10 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 11 | 11 | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp index d325d31c7..237a5af3f 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <utility> | 5 | #include <utility> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 7 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 8 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/modifiers.h" | 9 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 10 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 11 | 11 | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp index 8aa494a4d..f698b8b9b 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 5 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/value.h" | 7 | #include "shader_recompiler/frontend/ir/value.h" |
| 8 | 8 | ||
| 9 | namespace Shader::Backend::GLASM { | 9 | namespace Shader::Backend::GLASM { |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp index e69de29bb..eed7bfec2 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp | |||
| @@ -0,0 +1,26 @@ | |||
| 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 "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::GLASM { | ||
| 9 | |||
| 10 | void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) { | ||
| 11 | ctx.Add("OR.S {},{},{};", inst, a, b); | ||
| 12 | } | ||
| 13 | |||
| 14 | void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) { | ||
| 15 | ctx.Add("AND.S {},{},{};", inst, a, b); | ||
| 16 | } | ||
| 17 | |||
| 18 | void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) { | ||
| 19 | ctx.Add("XOR.S {},{},{};", inst, a, b); | ||
| 20 | } | ||
| 21 | |||
| 22 | void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) { | ||
| 23 | ctx.Add("SEQ.S {},{},0;", inst, value); | ||
| 24 | } | ||
| 25 | |||
| 26 | } // namespace Shader::Backend::GLASM | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp index af9fac7c1..f135b67f5 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 7 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 8 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/program.h" | 9 | #include "shader_recompiler/frontend/ir/program.h" |
| 10 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 11 | #include "shader_recompiler/runtime_info.h" | 11 | #include "shader_recompiler/runtime_info.h" |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp index 681aeda8d..86287ee3f 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 7 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 8 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/program.h" | 9 | #include "shader_recompiler/frontend/ir/program.h" |
| 10 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 11 | 11 | ||
| @@ -17,110 +17,6 @@ namespace Shader::Backend::GLASM { | |||
| 17 | 17 | ||
| 18 | #define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) | 18 | #define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) |
| 19 | 19 | ||
| 20 | static void DefinePhi(EmitContext& ctx, IR::Inst& phi) { | ||
| 21 | switch (phi.Type()) { | ||
| 22 | case IR::Type::U1: | ||
| 23 | case IR::Type::U32: | ||
| 24 | case IR::Type::F32: | ||
| 25 | ctx.reg_alloc.Define(phi); | ||
| 26 | break; | ||
| 27 | case IR::Type::U64: | ||
| 28 | case IR::Type::F64: | ||
| 29 | ctx.reg_alloc.LongDefine(phi); | ||
| 30 | break; | ||
| 31 | default: | ||
| 32 | throw NotImplementedException("Phi node type {}", phi.Type()); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | void EmitPhi(EmitContext& ctx, IR::Inst& phi) { | ||
| 37 | const size_t num_args{phi.NumArgs()}; | ||
| 38 | for (size_t i = 0; i < num_args; ++i) { | ||
| 39 | ctx.reg_alloc.Consume(phi.Arg(i)); | ||
| 40 | } | ||
| 41 | if (!phi.Definition<Id>().is_valid) { | ||
| 42 | // The phi node wasn't forward defined | ||
| 43 | DefinePhi(ctx, phi); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | void EmitVoid(EmitContext&) {} | ||
| 48 | |||
| 49 | void EmitReference(EmitContext& ctx, const IR::Value& value) { | ||
| 50 | ctx.reg_alloc.Consume(value); | ||
| 51 | } | ||
| 52 | |||
| 53 | void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) { | ||
| 54 | IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())}; | ||
| 55 | if (!phi.Definition<Id>().is_valid) { | ||
| 56 | // The phi node wasn't forward defined | ||
| 57 | DefinePhi(ctx, phi); | ||
| 58 | } | ||
| 59 | const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})}; | ||
| 60 | const Value eval_value{ctx.reg_alloc.Consume(value)}; | ||
| 61 | |||
| 62 | if (phi_reg == eval_value) { | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | switch (phi.Flags<IR::Type>()) { | ||
| 66 | case IR::Type::U1: | ||
| 67 | case IR::Type::U32: | ||
| 68 | case IR::Type::F32: | ||
| 69 | ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value}); | ||
| 70 | break; | ||
| 71 | case IR::Type::U64: | ||
| 72 | case IR::Type::F64: | ||
| 73 | ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value}); | ||
| 74 | break; | ||
| 75 | default: | ||
| 76 | throw NotImplementedException("Phi node type {}", phi.Type()); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | void EmitJoin(EmitContext& ctx) { | ||
| 81 | NotImplemented(); | ||
| 82 | } | ||
| 83 | |||
| 84 | void EmitDemoteToHelperInvocation(EmitContext& ctx) { | ||
| 85 | ctx.Add("KIL TR.x;"); | ||
| 86 | } | ||
| 87 | |||
| 88 | void EmitBarrier(EmitContext& ctx) { | ||
| 89 | ctx.Add("BAR;"); | ||
| 90 | } | ||
| 91 | |||
| 92 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx) { | ||
| 93 | ctx.Add("MEMBAR.CTA;"); | ||
| 94 | } | ||
| 95 | |||
| 96 | void EmitDeviceMemoryBarrier(EmitContext& ctx) { | ||
| 97 | ctx.Add("MEMBAR;"); | ||
| 98 | } | ||
| 99 | |||
| 100 | void EmitPrologue(EmitContext& ctx) { | ||
| 101 | // TODO | ||
| 102 | } | ||
| 103 | |||
| 104 | void EmitEpilogue(EmitContext& ctx) { | ||
| 105 | // TODO | ||
| 106 | } | ||
| 107 | |||
| 108 | void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) { | ||
| 109 | if (stream.type == Type::U32 && stream.imm_u32 == 0) { | ||
| 110 | ctx.Add("EMIT;"); | ||
| 111 | } else { | ||
| 112 | ctx.Add("EMITS {};", stream); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { | ||
| 117 | if (!stream.IsImmediate()) { | ||
| 118 | LOG_WARNING(Shader_GLASM, "Stream is not immediate"); | ||
| 119 | } | ||
| 120 | ctx.reg_alloc.Consume(stream); | ||
| 121 | ctx.Add("ENDPRIM;"); | ||
| 122 | } | ||
| 123 | |||
| 124 | void EmitGetRegister(EmitContext& ctx) { | 20 | void EmitGetRegister(EmitContext& ctx) { |
| 125 | NotImplemented(); | 21 | NotImplemented(); |
| 126 | } | 22 | } |
| @@ -185,55 +81,6 @@ void EmitSetOFlag(EmitContext& ctx) { | |||
| 185 | NotImplemented(); | 81 | NotImplemented(); |
| 186 | } | 82 | } |
| 187 | 83 | ||
| 188 | void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) { | ||
| 189 | ctx.Add("MOV.S {},invocation.groupid;", inst); | ||
| 190 | } | ||
| 191 | |||
| 192 | void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) { | ||
| 193 | ctx.Add("MOV.S {},invocation.localid;", inst); | ||
| 194 | } | ||
| 195 | |||
| 196 | void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) { | ||
| 197 | ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst); | ||
| 198 | } | ||
| 199 | |||
| 200 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { | ||
| 201 | ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst); | ||
| 202 | } | ||
| 203 | |||
| 204 | void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) { | ||
| 205 | ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst); | ||
| 206 | } | ||
| 207 | |||
| 208 | void EmitYDirection(EmitContext& ctx, IR::Inst& inst) { | ||
| 209 | ctx.uses_y_direction = true; | ||
| 210 | ctx.Add("MOV.F {}.x,y_direction[0].w;", inst); | ||
| 211 | } | ||
| 212 | |||
| 213 | void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { | ||
| 214 | ctx.Add("MOV.F {}.x,scaling[0].z;", inst); | ||
| 215 | } | ||
| 216 | |||
| 217 | void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) { | ||
| 218 | ctx.Add("MOV.S {}.x,0;", inst); | ||
| 219 | } | ||
| 220 | |||
| 221 | void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) { | ||
| 222 | ctx.Add("MOV.S {}.x,0;", inst); | ||
| 223 | } | ||
| 224 | |||
| 225 | void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) { | ||
| 226 | ctx.Add("MOV.S {}.x,0;", inst); | ||
| 227 | } | ||
| 228 | |||
| 229 | void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) { | ||
| 230 | ctx.Add("MOV.S {}.x,0;", inst); | ||
| 231 | } | ||
| 232 | |||
| 233 | void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) { | ||
| 234 | ctx.LongAdd("MOV.S64 {}.x,0;", inst); | ||
| 235 | } | ||
| 236 | |||
| 237 | void EmitGetZeroFromOp(EmitContext& ctx) { | 84 | void EmitGetZeroFromOp(EmitContext& ctx) { |
| 238 | NotImplemented(); | 85 | NotImplemented(); |
| 239 | } | 86 | } |
| @@ -258,20 +105,4 @@ void EmitGetInBoundsFromOp(EmitContext& ctx) { | |||
| 258 | NotImplemented(); | 105 | NotImplemented(); |
| 259 | } | 106 | } |
| 260 | 107 | ||
| 261 | void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) { | ||
| 262 | ctx.Add("OR.S {},{},{};", inst, a, b); | ||
| 263 | } | ||
| 264 | |||
| 265 | void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) { | ||
| 266 | ctx.Add("AND.S {},{},{};", inst, a, b); | ||
| 267 | } | ||
| 268 | |||
| 269 | void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) { | ||
| 270 | ctx.Add("XOR.S {},{},{};", inst, a, b); | ||
| 271 | } | ||
| 272 | |||
| 273 | void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) { | ||
| 274 | ctx.Add("SEQ.S {},{},0;", inst, value); | ||
| 275 | } | ||
| 276 | |||
| 277 | } // namespace Shader::Backend::GLASM | 108 | } // namespace Shader::Backend::GLASM |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp index 68fff613c..dc441c56d 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | // Licensed under GPLv2 or any later version | 3 | // Licensed under GPLv2 or any later version |
| 4 | // Refer to the license.txt file included. | 4 | // Refer to the license.txt file included. |
| 5 | 5 | ||
| 6 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 7 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 6 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 7 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 8 | #include "shader_recompiler/frontend/ir/value.h" | 8 | #include "shader_recompiler/frontend/ir/value.h" |
| 9 | 9 | ||
| 10 | namespace Shader::Backend::GLASM { | 10 | namespace Shader::Backend::GLASM { |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp index c1498f449..39e1c6c3a 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | // Licensed under GPLv2 or any later version | 3 | // Licensed under GPLv2 or any later version |
| 4 | // Refer to the license.txt file included. | 4 | // Refer to the license.txt file included. |
| 5 | 5 | ||
| 6 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 7 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 6 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 7 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 8 | #include "shader_recompiler/frontend/ir/value.h" | 8 | #include "shader_recompiler/frontend/ir/value.h" |
| 9 | 9 | ||
| 10 | namespace Shader::Backend::GLASM { | 10 | namespace Shader::Backend::GLASM { |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp index e69de29bb..e7a5fb13a 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp | |||
| @@ -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 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 8 | |||
| 9 | namespace Shader::Backend::GLASM { | ||
| 10 | |||
| 11 | static void DefinePhi(EmitContext& ctx, IR::Inst& phi) { | ||
| 12 | switch (phi.Type()) { | ||
| 13 | case IR::Type::U1: | ||
| 14 | case IR::Type::U32: | ||
| 15 | case IR::Type::F32: | ||
| 16 | ctx.reg_alloc.Define(phi); | ||
| 17 | break; | ||
| 18 | case IR::Type::U64: | ||
| 19 | case IR::Type::F64: | ||
| 20 | ctx.reg_alloc.LongDefine(phi); | ||
| 21 | break; | ||
| 22 | default: | ||
| 23 | throw NotImplementedException("Phi node type {}", phi.Type()); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | void EmitPhi(EmitContext& ctx, IR::Inst& phi) { | ||
| 28 | const size_t num_args{phi.NumArgs()}; | ||
| 29 | for (size_t i = 0; i < num_args; ++i) { | ||
| 30 | ctx.reg_alloc.Consume(phi.Arg(i)); | ||
| 31 | } | ||
| 32 | if (!phi.Definition<Id>().is_valid) { | ||
| 33 | // The phi node wasn't forward defined | ||
| 34 | DefinePhi(ctx, phi); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | void EmitVoid(EmitContext&) {} | ||
| 39 | |||
| 40 | void EmitReference(EmitContext& ctx, const IR::Value& value) { | ||
| 41 | ctx.reg_alloc.Consume(value); | ||
| 42 | } | ||
| 43 | |||
| 44 | void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) { | ||
| 45 | IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())}; | ||
| 46 | if (!phi.Definition<Id>().is_valid) { | ||
| 47 | // The phi node wasn't forward defined | ||
| 48 | DefinePhi(ctx, phi); | ||
| 49 | } | ||
| 50 | const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})}; | ||
| 51 | const Value eval_value{ctx.reg_alloc.Consume(value)}; | ||
| 52 | |||
| 53 | if (phi_reg == eval_value) { | ||
| 54 | return; | ||
| 55 | } | ||
| 56 | switch (phi.Flags<IR::Type>()) { | ||
| 57 | case IR::Type::U1: | ||
| 58 | case IR::Type::U32: | ||
| 59 | case IR::Type::F32: | ||
| 60 | ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value}); | ||
| 61 | break; | ||
| 62 | case IR::Type::U64: | ||
| 63 | case IR::Type::F64: | ||
| 64 | ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value}); | ||
| 65 | break; | ||
| 66 | default: | ||
| 67 | throw NotImplementedException("Phi node type {}", phi.Type()); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | void EmitPrologue(EmitContext&) { | ||
| 72 | // TODO | ||
| 73 | } | ||
| 74 | |||
| 75 | void EmitEpilogue(EmitContext&) { | ||
| 76 | // TODO | ||
| 77 | } | ||
| 78 | |||
| 79 | void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) { | ||
| 80 | if (stream.type == Type::U32 && stream.imm_u32 == 0) { | ||
| 81 | ctx.Add("EMIT;"); | ||
| 82 | } else { | ||
| 83 | ctx.Add("EMITS {};", stream); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { | ||
| 88 | if (!stream.IsImmediate()) { | ||
| 89 | LOG_WARNING(Shader_GLASM, "Stream is not immediate"); | ||
| 90 | } | ||
| 91 | ctx.reg_alloc.Consume(stream); | ||
| 92 | ctx.Add("ENDPRIM;"); | ||
| 93 | } | ||
| 94 | |||
| 95 | } // namespace Shader::Backend::GLASM | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp index e69de29bb..875e9d991 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp | |||
| @@ -0,0 +1,30 @@ | |||
| 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 "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::GLASM { | ||
| 9 | |||
| 10 | void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) { | ||
| 11 | ctx.Add("MOV.S {}.x,0;", inst); | ||
| 12 | } | ||
| 13 | |||
| 14 | void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) { | ||
| 15 | ctx.Add("MOV.S {}.x,0;", inst); | ||
| 16 | } | ||
| 17 | |||
| 18 | void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) { | ||
| 19 | ctx.Add("MOV.S {}.x,0;", inst); | ||
| 20 | } | ||
| 21 | |||
| 22 | void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) { | ||
| 23 | ctx.Add("MOV.S {}.x,0;", inst); | ||
| 24 | } | ||
| 25 | |||
| 26 | void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) { | ||
| 27 | ctx.LongAdd("MOV.S64 {}.x,0;", inst); | ||
| 28 | } | ||
| 29 | |||
| 30 | } // namespace Shader::Backend::GLASM | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp index 544d475b4..32e0dd923 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 6 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" | 5 | #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" |
| 6 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/value.h" | 7 | #include "shader_recompiler/frontend/ir/value.h" |
| 8 | #include "shader_recompiler/profile.h" | 8 | #include "shader_recompiler/profile.h" |
| 9 | 9 | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_context.cpp b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp index 8fd459dfe..0401953f7 100644 --- a/src/shader_recompiler/backend/glasm/emit_context.cpp +++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/bindings.h" | 7 | #include "shader_recompiler/backend/bindings.h" |
| 8 | #include "shader_recompiler/backend/glasm/emit_context.h" | ||
| 9 | #include "shader_recompiler/backend/glasm/emit_glasm.h" | 8 | #include "shader_recompiler/backend/glasm/emit_glasm.h" |
| 9 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | ||
| 10 | #include "shader_recompiler/frontend/ir/program.h" | 10 | #include "shader_recompiler/frontend/ir/program.h" |
| 11 | #include "shader_recompiler/profile.h" | 11 | #include "shader_recompiler/profile.h" |
| 12 | #include "shader_recompiler/runtime_info.h" | 12 | #include "shader_recompiler/runtime_info.h" |
diff --git a/src/shader_recompiler/backend/glasm/emit_context.h b/src/shader_recompiler/backend/glasm/glasm_emit_context.h index 8433e5c00..8433e5c00 100644 --- a/src/shader_recompiler/backend/glasm/emit_context.h +++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.h | |||
diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.cpp b/src/shader_recompiler/backend/glasm/reg_alloc.cpp index 4c046db6e..201e428c1 100644 --- a/src/shader_recompiler/backend/glasm/reg_alloc.cpp +++ b/src/shader_recompiler/backend/glasm/reg_alloc.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <fmt/format.h> | 7 | #include <fmt/format.h> |
| 8 | 8 | ||
| 9 | #include "shader_recompiler/backend/glasm/emit_context.h" | 9 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" |
| 10 | #include "shader_recompiler/backend/glasm/reg_alloc.h" | 10 | #include "shader_recompiler/backend/glasm/reg_alloc.h" |
| 11 | #include "shader_recompiler/exception.h" | 11 | #include "shader_recompiler/exception.h" |
| 12 | #include "shader_recompiler/frontend/ir/value.h" | 12 | #include "shader_recompiler/frontend/ir/value.h" |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp index 8a430d573..78b2eeaa2 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp | |||
| @@ -9,9 +9,9 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/div_ceil.h" | 10 | #include "common/div_ceil.h" |
| 11 | #include "common/settings.h" | 11 | #include "common/settings.h" |
| 12 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 13 | #include "shader_recompiler/backend/glsl/emit_glsl.h" | 12 | #include "shader_recompiler/backend/glsl/emit_glsl.h" |
| 14 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 13 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 14 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 15 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 15 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 16 | 16 | ||
| 17 | namespace Shader::Backend::GLSL { | 17 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp index 772acc5a4..dc377b053 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp index e1d1b558e..8a9faa394 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 6 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 5 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 6 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/value.h" | 7 | #include "shader_recompiler/frontend/ir/value.h" |
| 8 | 8 | ||
| 9 | namespace Shader::Backend::GLSL { | 9 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp index 3c1714e89..0f2668d9e 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp index 49a66e3ec..98cc57e58 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index 4c26f3829..1920047f4 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | #include "shader_recompiler/profile.h" | 10 | #include "shader_recompiler/profile.h" |
| 11 | #include "shader_recompiler/runtime_info.h" | 11 | #include "shader_recompiler/runtime_info.h" |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp index 53f8896be..c86465e8b 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/exception.h" | 9 | #include "shader_recompiler/exception.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp index eeae6562c..ce6ea1bb7 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp index d423bfb1b..b765a251b 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/modifiers.h" | 9 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 10 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 11 | 11 | ||
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp index 2f78d0267..fae2e397a 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/modifiers.h" | 9 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 10 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 11 | #include "shader_recompiler/profile.h" | 11 | #include "shader_recompiler/profile.h" |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp index 88c1d4c5e..44060df33 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp index 338ff4bd6..742fec9cf 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp index e3957491f..9fd41b4fd 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | #include "shader_recompiler/profile.h" | 10 | #include "shader_recompiler/profile.h" |
| 11 | 11 | ||
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp index f420fe388..4ebdfb3bc 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | #ifdef _MSC_VER | 11 | #ifdef _MSC_VER |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp index 49fba9073..b1e486e5f 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp index 518b78f06..74ae345e5 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | 10 | ||
| 11 | namespace Shader::Backend::GLSL { | 11 | namespace Shader::Backend::GLSL { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp index 67f9dad68..b8ddafe48 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/program.h" | 9 | #include "shader_recompiler/frontend/ir/program.h" |
| 10 | #include "shader_recompiler/frontend/ir/value.h" | 10 | #include "shader_recompiler/frontend/ir/value.h" |
| 11 | #include "shader_recompiler/profile.h" | 11 | #include "shader_recompiler/profile.h" |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp index 15bf02dd6..cace1db85 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | 9 | ||
| 10 | namespace Shader::Backend::GLSL { | 10 | namespace Shader::Backend::GLSL { |
| 11 | 11 | ||
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp index cd285e2c8..6e01979b4 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/glsl/emit_context.h" | ||
| 8 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | 7 | #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" |
| 8 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 9 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | #include "shader_recompiler/profile.h" | 10 | #include "shader_recompiler/profile.h" |
| 11 | 11 | ||
diff --git a/src/shader_recompiler/backend/glsl/emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 97bd59302..1de017e76 100644 --- a/src/shader_recompiler/backend/glsl/emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/bindings.h" | 5 | #include "shader_recompiler/backend/bindings.h" |
| 6 | #include "shader_recompiler/backend/glsl/emit_context.h" | 6 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" |
| 7 | #include "shader_recompiler/frontend/ir/program.h" | 7 | #include "shader_recompiler/frontend/ir/program.h" |
| 8 | #include "shader_recompiler/profile.h" | 8 | #include "shader_recompiler/profile.h" |
| 9 | #include "shader_recompiler/runtime_info.h" | 9 | #include "shader_recompiler/runtime_info.h" |
diff --git a/src/shader_recompiler/backend/glsl/emit_context.h b/src/shader_recompiler/backend/glsl/glsl_emit_context.h index d9b639d29..d9b639d29 100644 --- a/src/shader_recompiler/backend/glsl/emit_context.h +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.h | |||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index d7a86e270..6ce7ed12a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "common/settings.h" | 11 | #include "common/settings.h" |
| 12 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 12 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 13 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 13 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 14 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 14 | #include "shader_recompiler/frontend/ir/basic_block.h" | 15 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 15 | #include "shader_recompiler/frontend/ir/program.h" | 16 | #include "shader_recompiler/frontend/ir/program.h" |
| 16 | 17 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 4b25534ce..b412957c7 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -6,13 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include <sirit/sirit.h> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 12 | #include "shader_recompiler/backend/bindings.h" | 10 | #include "shader_recompiler/backend/bindings.h" |
| 13 | #include "shader_recompiler/backend/spirv/emit_context.h" | ||
| 14 | #include "shader_recompiler/frontend/ir/program.h" | 11 | #include "shader_recompiler/frontend/ir/program.h" |
| 15 | #include "shader_recompiler/profile.h" | 12 | #include "shader_recompiler/profile.h" |
| 13 | #include "shader_recompiler/runtime_info.h" | ||
| 16 | 14 | ||
| 17 | namespace Shader::Backend::SPIRV { | 15 | namespace Shader::Backend::SPIRV { |
| 18 | 16 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 9af8bb9e1..0d37b405c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | namespace { | 10 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp index e0b52a001..9ce95a41b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/modifiers.h" | 8 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 8 | 9 | ||
| 9 | namespace Shader::Backend::SPIRV { | 10 | namespace Shader::Backend::SPIRV { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp index bb11f4f4e..02d1e63f7 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | 10 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp index 10ff4ecab..5c3e1ee2b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/modifiers.h" | 8 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 8 | 9 | ||
| 9 | namespace Shader::Backend::SPIRV { | 10 | namespace Shader::Backend::SPIRV { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index bac683ae1..ad84966b5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 8 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 9 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 9 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 10 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 10 | 11 | ||
| 11 | namespace Shader::Backend::SPIRV { | 12 | namespace Shader::Backend::SPIRV { |
| 12 | namespace { | 13 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp index d33486f28..1eca3aa85 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | 10 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp index fd42b7a16..832de2452 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | namespace { | 10 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp index 61cf25f9c..0cdc46495 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/modifiers.h" | 8 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 8 | 9 | ||
| 9 | namespace Shader::Backend::SPIRV { | 10 | namespace Shader::Backend::SPIRV { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 4d168a96d..d18d5f1d5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 8 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 8 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 9 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/modifiers.h" | 10 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 10 | 11 | ||
| 11 | namespace Shader::Backend::SPIRV { | 12 | namespace Shader::Backend::SPIRV { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp index d7f1a365a..a96190bc6 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/modifiers.h" | 8 | #include "shader_recompiler/frontend/ir/modifiers.h" |
| 8 | 9 | ||
| 9 | namespace Shader::Backend::SPIRV { | 10 | namespace Shader::Backend::SPIRV { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index 50277eec3..44521f539 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | namespace { | 10 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp index b9a9500fc..47745f7ee 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | 10 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp index 679ee2684..175f4be19 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 8 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 8 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 9 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 9 | 10 | ||
| 10 | namespace Shader::Backend::SPIRV { | 11 | namespace Shader::Backend::SPIRV { |
| 11 | namespace { | 12 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp index c5b4f4720..48caf1ffc 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | 10 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp index 9a79fc7a2..330c9052c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | namespace { | 10 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index 9e7eb3cb1..d96a17583 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | namespace { | 10 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp index c9f469e90..b5766fc52 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | 10 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp index cef52c56e..7034228bf 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" |
| 7 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 7 | 8 | ||
| 8 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 9 | namespace { | 10 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 723455462..4b6f792bf 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -13,8 +13,8 @@ | |||
| 13 | 13 | ||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/div_ceil.h" | 15 | #include "common/div_ceil.h" |
| 16 | #include "shader_recompiler/backend/spirv/emit_context.h" | ||
| 17 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 16 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 17 | #include "shader_recompiler/backend/spirv/spirv_emit_context.h" | ||
| 18 | 18 | ||
| 19 | namespace Shader::Backend::SPIRV { | 19 | namespace Shader::Backend::SPIRV { |
| 20 | namespace { | 20 | namespace { |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 63f8185d9..63f8185d9 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h | |||
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index 8369d0d84..b4df73e8a 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #pragma once | 5 | #pragma once |
| 2 | 6 | ||
| 3 | #include <array> | 7 | #include <array> |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 91a30fef7..6a6325e38 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -3,6 +3,7 @@ add_subdirectory(host_shaders) | |||
| 3 | if(LIBVA_FOUND) | 3 | if(LIBVA_FOUND) |
| 4 | set_source_files_properties(command_classes/codecs/codec.cpp | 4 | set_source_files_properties(command_classes/codecs/codec.cpp |
| 5 | PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) | 5 | PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) |
| 6 | list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) | ||
| 6 | endif() | 7 | endif() |
| 7 | 8 | ||
| 8 | add_library(video_core STATIC | 9 | add_library(video_core STATIC |
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 916277811..2a532b883 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cstdio> | ||
| 5 | #include <fstream> | 7 | #include <fstream> |
| 6 | #include <vector> | 8 | #include <vector> |
| 7 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| @@ -15,12 +17,28 @@ | |||
| 15 | 17 | ||
| 16 | extern "C" { | 18 | extern "C" { |
| 17 | #include <libavutil/opt.h> | 19 | #include <libavutil/opt.h> |
| 20 | #ifdef LIBVA_FOUND | ||
| 21 | // for querying VAAPI driver information | ||
| 22 | #include <libavutil/hwcontext_vaapi.h> | ||
| 23 | #endif | ||
| 18 | } | 24 | } |
| 19 | 25 | ||
| 20 | namespace Tegra { | 26 | namespace Tegra { |
| 21 | namespace { | 27 | namespace { |
| 22 | constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; | 28 | constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; |
| 23 | constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; | 29 | constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; |
| 30 | constexpr std::array PREFERRED_GPU_DECODERS = { | ||
| 31 | AV_HWDEVICE_TYPE_CUDA, | ||
| 32 | #ifdef _WIN32 | ||
| 33 | AV_HWDEVICE_TYPE_D3D11VA, | ||
| 34 | AV_HWDEVICE_TYPE_DXVA2, | ||
| 35 | #elif defined(__linux__) | ||
| 36 | AV_HWDEVICE_TYPE_VAAPI, | ||
| 37 | AV_HWDEVICE_TYPE_VDPAU, | ||
| 38 | #endif | ||
| 39 | // last resort for Linux Flatpak (w/ NVIDIA) | ||
| 40 | AV_HWDEVICE_TYPE_VULKAN, | ||
| 41 | }; | ||
| 24 | 42 | ||
| 25 | void AVPacketDeleter(AVPacket* ptr) { | 43 | void AVPacketDeleter(AVPacket* ptr) { |
| 26 | av_packet_free(&ptr); | 44 | av_packet_free(&ptr); |
| @@ -59,46 +77,50 @@ Codec::~Codec() { | |||
| 59 | av_buffer_unref(&av_gpu_decoder); | 77 | av_buffer_unref(&av_gpu_decoder); |
| 60 | } | 78 | } |
| 61 | 79 | ||
| 80 | // List all the currently available hwcontext in ffmpeg | ||
| 81 | static std::vector<AVHWDeviceType> ListSupportedContexts() { | ||
| 82 | std::vector<AVHWDeviceType> contexts{}; | ||
| 83 | AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; | ||
| 84 | do { | ||
| 85 | current_device_type = av_hwdevice_iterate_types(current_device_type); | ||
| 86 | contexts.push_back(current_device_type); | ||
| 87 | } while (current_device_type != AV_HWDEVICE_TYPE_NONE); | ||
| 88 | return contexts; | ||
| 89 | } | ||
| 90 | |||
| 62 | bool Codec::CreateGpuAvDevice() { | 91 | bool Codec::CreateGpuAvDevice() { |
| 63 | #if defined(LIBVA_FOUND) | ||
| 64 | static constexpr std::array<const char*, 3> VAAPI_DRIVERS = { | ||
| 65 | "i915", | ||
| 66 | "iHD", | ||
| 67 | "amdgpu", | ||
| 68 | }; | ||
| 69 | AVDictionary* hwdevice_options = nullptr; | ||
| 70 | av_dict_set(&hwdevice_options, "connection_type", "drm", 0); | ||
| 71 | for (const auto& driver : VAAPI_DRIVERS) { | ||
| 72 | av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); | ||
| 73 | const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI, | ||
| 74 | nullptr, hwdevice_options, 0); | ||
| 75 | if (hwdevice_error >= 0) { | ||
| 76 | LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); | ||
| 77 | av_dict_free(&hwdevice_options); | ||
| 78 | av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI; | ||
| 79 | return true; | ||
| 80 | } | ||
| 81 | LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); | ||
| 82 | } | ||
| 83 | LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); | ||
| 84 | av_dict_free(&hwdevice_options); | ||
| 85 | #endif | ||
| 86 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; | 92 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; |
| 87 | static constexpr std::array GPU_DECODER_TYPES{ | 93 | static const auto supported_contexts = ListSupportedContexts(); |
| 88 | AV_HWDEVICE_TYPE_CUDA, | 94 | for (const auto& type : PREFERRED_GPU_DECODERS) { |
| 89 | #ifdef _WIN32 | 95 | if (std::none_of(supported_contexts.begin(), supported_contexts.end(), |
| 90 | AV_HWDEVICE_TYPE_D3D11VA, | 96 | [&type](const auto& context) { return context == type; })) { |
| 91 | #else | 97 | LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); |
| 92 | AV_HWDEVICE_TYPE_VDPAU, | 98 | continue; |
| 93 | #endif | 99 | } |
| 94 | }; | ||
| 95 | for (const auto& type : GPU_DECODER_TYPES) { | ||
| 96 | const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); | 100 | const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); |
| 97 | if (hwdevice_res < 0) { | 101 | if (hwdevice_res < 0) { |
| 98 | LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", | 102 | LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", |
| 99 | av_hwdevice_get_type_name(type), hwdevice_res); | 103 | av_hwdevice_get_type_name(type), hwdevice_res); |
| 100 | continue; | 104 | continue; |
| 101 | } | 105 | } |
| 106 | #ifdef LIBVA_FOUND | ||
| 107 | if (type == AV_HWDEVICE_TYPE_VAAPI) { | ||
| 108 | // we need to determine if this is an impersonated VAAPI driver | ||
| 109 | AVHWDeviceContext* hwctx = | ||
| 110 | static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data)); | ||
| 111 | AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx); | ||
| 112 | const char* vendor_name = vaQueryVendorString(vactx->display); | ||
| 113 | if (strstr(vendor_name, "VDPAU backend")) { | ||
| 114 | // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them | ||
| 115 | LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver"); | ||
| 116 | continue; | ||
| 117 | } else { | ||
| 118 | // according to some user testing, certain vaapi driver (Intel?) could be buggy | ||
| 119 | // so let's log the driver name which may help the developers/supporters | ||
| 120 | LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | #endif | ||
| 102 | for (int i = 0;; i++) { | 124 | for (int i = 0;; i++) { |
| 103 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); | 125 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); |
| 104 | if (!config) { | 126 | if (!config) { |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index ab7c21a49..8788f5148 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -185,16 +185,6 @@ struct GPU::Impl { | |||
| 185 | return *dma_pusher; | 185 | return *dma_pusher; |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | /// Returns a reference to the GPU CDMA pusher. | ||
| 189 | [[nodiscard]] Tegra::CDmaPusher& CDmaPusher() { | ||
| 190 | return *cdma_pusher; | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Returns a const reference to the GPU CDMA pusher. | ||
| 194 | [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const { | ||
| 195 | return *cdma_pusher; | ||
| 196 | } | ||
| 197 | |||
| 198 | /// Returns a reference to the underlying renderer. | 188 | /// Returns a reference to the underlying renderer. |
| 199 | [[nodiscard]] VideoCore::RendererBase& Renderer() { | 189 | [[nodiscard]] VideoCore::RendererBase& Renderer() { |
| 200 | return *renderer; | 190 | return *renderer; |
| @@ -338,25 +328,27 @@ struct GPU::Impl { | |||
| 338 | } | 328 | } |
| 339 | 329 | ||
| 340 | /// Push GPU command buffer entries to be processed | 330 | /// Push GPU command buffer entries to be processed |
| 341 | void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { | 331 | void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) { |
| 342 | if (!use_nvdec) { | 332 | if (!use_nvdec) { |
| 343 | return; | 333 | return; |
| 344 | } | 334 | } |
| 345 | 335 | ||
| 346 | if (!cdma_pusher) { | 336 | if (!cdma_pushers.contains(id)) { |
| 347 | cdma_pusher = std::make_unique<Tegra::CDmaPusher>(gpu); | 337 | cdma_pushers.insert_or_assign(id, std::make_unique<Tegra::CDmaPusher>(gpu)); |
| 348 | } | 338 | } |
| 349 | 339 | ||
| 350 | // SubmitCommandBuffer would make the nvdec operations async, this is not currently working | 340 | // SubmitCommandBuffer would make the nvdec operations async, this is not currently working |
| 351 | // TODO(ameerj): RE proper async nvdec operation | 341 | // TODO(ameerj): RE proper async nvdec operation |
| 352 | // gpu_thread.SubmitCommandBuffer(std::move(entries)); | 342 | // gpu_thread.SubmitCommandBuffer(std::move(entries)); |
| 353 | 343 | cdma_pushers[id]->ProcessEntries(std::move(entries)); | |
| 354 | cdma_pusher->ProcessEntries(std::move(entries)); | ||
| 355 | } | 344 | } |
| 356 | 345 | ||
| 357 | /// Frees the CDMAPusher instance to free up resources | 346 | /// Frees the CDMAPusher instance to free up resources |
| 358 | void ClearCdmaInstance() { | 347 | void ClearCdmaInstance(u32 id) { |
| 359 | cdma_pusher.reset(); | 348 | const auto iter = cdma_pushers.find(id); |
| 349 | if (iter != cdma_pushers.end()) { | ||
| 350 | cdma_pushers.erase(iter); | ||
| 351 | } | ||
| 360 | } | 352 | } |
| 361 | 353 | ||
| 362 | /// Swap buffers (render frame) | 354 | /// Swap buffers (render frame) |
| @@ -659,7 +651,7 @@ struct GPU::Impl { | |||
| 659 | Core::System& system; | 651 | Core::System& system; |
| 660 | std::unique_ptr<Tegra::MemoryManager> memory_manager; | 652 | std::unique_ptr<Tegra::MemoryManager> memory_manager; |
| 661 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; | 653 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; |
| 662 | std::unique_ptr<Tegra::CDmaPusher> cdma_pusher; | 654 | std::map<u32, std::unique_ptr<Tegra::CDmaPusher>> cdma_pushers; |
| 663 | std::unique_ptr<VideoCore::RendererBase> renderer; | 655 | std::unique_ptr<VideoCore::RendererBase> renderer; |
| 664 | VideoCore::RasterizerInterface* rasterizer = nullptr; | 656 | VideoCore::RasterizerInterface* rasterizer = nullptr; |
| 665 | const bool use_nvdec; | 657 | const bool use_nvdec; |
| @@ -811,14 +803,6 @@ const Tegra::DmaPusher& GPU::DmaPusher() const { | |||
| 811 | return impl->DmaPusher(); | 803 | return impl->DmaPusher(); |
| 812 | } | 804 | } |
| 813 | 805 | ||
| 814 | Tegra::CDmaPusher& GPU::CDmaPusher() { | ||
| 815 | return impl->CDmaPusher(); | ||
| 816 | } | ||
| 817 | |||
| 818 | const Tegra::CDmaPusher& GPU::CDmaPusher() const { | ||
| 819 | return impl->CDmaPusher(); | ||
| 820 | } | ||
| 821 | |||
| 822 | VideoCore::RendererBase& GPU::Renderer() { | 806 | VideoCore::RendererBase& GPU::Renderer() { |
| 823 | return impl->Renderer(); | 807 | return impl->Renderer(); |
| 824 | } | 808 | } |
| @@ -887,12 +871,12 @@ void GPU::PushGPUEntries(Tegra::CommandList&& entries) { | |||
| 887 | impl->PushGPUEntries(std::move(entries)); | 871 | impl->PushGPUEntries(std::move(entries)); |
| 888 | } | 872 | } |
| 889 | 873 | ||
| 890 | void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { | 874 | void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) { |
| 891 | impl->PushCommandBuffer(entries); | 875 | impl->PushCommandBuffer(id, entries); |
| 892 | } | 876 | } |
| 893 | 877 | ||
| 894 | void GPU::ClearCdmaInstance() { | 878 | void GPU::ClearCdmaInstance(u32 id) { |
| 895 | impl->ClearCdmaInstance(); | 879 | impl->ClearCdmaInstance(id); |
| 896 | } | 880 | } |
| 897 | 881 | ||
| 898 | void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 882 | void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 05e5c94f3..500411176 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -83,6 +83,7 @@ enum class DepthFormat : u32 { | |||
| 83 | S8_UINT_Z24_UNORM = 0x14, | 83 | S8_UINT_Z24_UNORM = 0x14, |
| 84 | D24X8_UNORM = 0x15, | 84 | D24X8_UNORM = 0x15, |
| 85 | D24S8_UNORM = 0x16, | 85 | D24S8_UNORM = 0x16, |
| 86 | S8_UINT = 0x17, | ||
| 86 | D24C8_UNORM = 0x18, | 87 | D24C8_UNORM = 0x18, |
| 87 | D32_FLOAT_S8X24_UINT = 0x19, | 88 | D32_FLOAT_S8X24_UINT = 0x19, |
| 88 | }; | 89 | }; |
| @@ -241,10 +242,10 @@ public: | |||
| 241 | void PushGPUEntries(Tegra::CommandList&& entries); | 242 | void PushGPUEntries(Tegra::CommandList&& entries); |
| 242 | 243 | ||
| 243 | /// Push GPU command buffer entries to be processed | 244 | /// Push GPU command buffer entries to be processed |
| 244 | void PushCommandBuffer(Tegra::ChCommandHeaderList& entries); | 245 | void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries); |
| 245 | 246 | ||
| 246 | /// Frees the CDMAPusher instance to free up resources | 247 | /// Frees the CDMAPusher instance to free up resources |
| 247 | void ClearCdmaInstance(); | 248 | void ClearCdmaInstance(u32 id); |
| 248 | 249 | ||
| 249 | /// Swap buffers (render frame) | 250 | /// Swap buffers (render frame) |
| 250 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); | 251 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); |
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index d779a967a..fd3e41434 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt | |||
| @@ -10,6 +10,8 @@ set(SHADER_FILES | |||
| 10 | astc_decoder.comp | 10 | astc_decoder.comp |
| 11 | block_linear_unswizzle_2d.comp | 11 | block_linear_unswizzle_2d.comp |
| 12 | block_linear_unswizzle_3d.comp | 12 | block_linear_unswizzle_3d.comp |
| 13 | convert_abgr8_to_d24s8.frag | ||
| 14 | convert_d24s8_to_abgr8.frag | ||
| 13 | convert_depth_to_float.frag | 15 | convert_depth_to_float.frag |
| 14 | convert_float_to_depth.frag | 16 | convert_float_to_depth.frag |
| 15 | full_screen_triangle.vert | 17 | full_screen_triangle.vert |
diff --git a/src/video_core/host_shaders/convert_abgr8_to_d24s8.frag b/src/video_core/host_shaders/convert_abgr8_to_d24s8.frag new file mode 100644 index 000000000..ea055ddad --- /dev/null +++ b/src/video_core/host_shaders/convert_abgr8_to_d24s8.frag | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #version 450 | ||
| 6 | #extension GL_ARB_shader_stencil_export : require | ||
| 7 | |||
| 8 | layout(binding = 0) uniform sampler2D color_texture; | ||
| 9 | |||
| 10 | void main() { | ||
| 11 | ivec2 coord = ivec2(gl_FragCoord.xy); | ||
| 12 | uvec4 color = uvec4(texelFetch(color_texture, coord, 0).abgr * (exp2(8) - 1.0f)); | ||
| 13 | uvec4 bytes = color << uvec4(24, 16, 8, 0); | ||
| 14 | uint depth_stencil_unorm = bytes.x | bytes.y | bytes.z | bytes.w; | ||
| 15 | |||
| 16 | gl_FragDepth = float(depth_stencil_unorm & 0x00FFFFFFu) / (exp2(24.0) - 1.0f); | ||
| 17 | gl_FragStencilRefARB = int(depth_stencil_unorm >> 24); | ||
| 18 | } | ||
diff --git a/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag new file mode 100644 index 000000000..94368fb59 --- /dev/null +++ b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #version 450 | ||
| 6 | |||
| 7 | layout(binding = 0) uniform sampler2D depth_tex; | ||
| 8 | layout(binding = 1) uniform isampler2D stencil_tex; | ||
| 9 | |||
| 10 | layout(location = 0) out vec4 color; | ||
| 11 | |||
| 12 | void main() { | ||
| 13 | ivec2 coord = ivec2(gl_FragCoord.xy); | ||
| 14 | uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f)); | ||
| 15 | uint stencil = uint(textureLod(stencil_tex, coord, 0).r); | ||
| 16 | |||
| 17 | highp uint depth_val = | ||
| 18 | uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0)); | ||
| 19 | lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r; | ||
| 20 | highp uvec4 components = | ||
| 21 | uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu); | ||
| 22 | color.abgr = vec4(components) / (exp2(8.0) - 1.0); | ||
| 23 | } | ||
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 6956535e5..14e6522f2 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include <glad/glad.h> | 10 | #include <glad/glad.h> |
| 11 | 11 | ||
| 12 | #include "common/bit_util.h" | ||
| 12 | #include "common/literals.h" | 13 | #include "common/literals.h" |
| 13 | #include "common/settings.h" | 14 | #include "common/settings.h" |
| 14 | #include "video_core/renderer_opengl/gl_device.h" | 15 | #include "video_core/renderer_opengl/gl_device.h" |
| @@ -148,6 +149,8 @@ GLenum AttachmentType(PixelFormat format) { | |||
| 148 | switch (const SurfaceType type = VideoCore::Surface::GetFormatType(format); type) { | 149 | switch (const SurfaceType type = VideoCore::Surface::GetFormatType(format); type) { |
| 149 | case SurfaceType::Depth: | 150 | case SurfaceType::Depth: |
| 150 | return GL_DEPTH_ATTACHMENT; | 151 | return GL_DEPTH_ATTACHMENT; |
| 152 | case SurfaceType::Stencil: | ||
| 153 | return GL_STENCIL_ATTACHMENT; | ||
| 151 | case SurfaceType::DepthStencil: | 154 | case SurfaceType::DepthStencil: |
| 152 | return GL_DEPTH_STENCIL_ATTACHMENT; | 155 | return GL_DEPTH_STENCIL_ATTACHMENT; |
| 153 | default: | 156 | default: |
| @@ -317,13 +320,12 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) { | |||
| 317 | } | 320 | } |
| 318 | } | 321 | } |
| 319 | 322 | ||
| 320 | OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_format) { | 323 | OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_format, |
| 324 | GLsizei gl_num_levels) { | ||
| 321 | const GLenum target = ImageTarget(info); | 325 | const GLenum target = ImageTarget(info); |
| 322 | const GLsizei width = info.size.width; | 326 | const GLsizei width = info.size.width; |
| 323 | const GLsizei height = info.size.height; | 327 | const GLsizei height = info.size.height; |
| 324 | const GLsizei depth = info.size.depth; | 328 | const GLsizei depth = info.size.depth; |
| 325 | const int max_host_mip_levels = std::bit_width(info.size.width); | ||
| 326 | const GLsizei num_levels = std::min(info.resources.levels, max_host_mip_levels); | ||
| 327 | const GLsizei num_layers = info.resources.layers; | 329 | const GLsizei num_layers = info.resources.layers; |
| 328 | const GLsizei num_samples = info.num_samples; | 330 | const GLsizei num_samples = info.num_samples; |
| 329 | 331 | ||
| @@ -335,10 +337,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form | |||
| 335 | } | 337 | } |
| 336 | switch (target) { | 338 | switch (target) { |
| 337 | case GL_TEXTURE_1D_ARRAY: | 339 | case GL_TEXTURE_1D_ARRAY: |
| 338 | glTextureStorage2D(handle, num_levels, gl_internal_format, width, num_layers); | 340 | glTextureStorage2D(handle, gl_num_levels, gl_internal_format, width, num_layers); |
| 339 | break; | 341 | break; |
| 340 | case GL_TEXTURE_2D_ARRAY: | 342 | case GL_TEXTURE_2D_ARRAY: |
| 341 | glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, num_layers); | 343 | glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, num_layers); |
| 342 | break; | 344 | break; |
| 343 | case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { | 345 | case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { |
| 344 | // TODO: Where should 'fixedsamplelocations' come from? | 346 | // TODO: Where should 'fixedsamplelocations' come from? |
| @@ -348,10 +350,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form | |||
| 348 | break; | 350 | break; |
| 349 | } | 351 | } |
| 350 | case GL_TEXTURE_RECTANGLE: | 352 | case GL_TEXTURE_RECTANGLE: |
| 351 | glTextureStorage2D(handle, num_levels, gl_internal_format, width, height); | 353 | glTextureStorage2D(handle, gl_num_levels, gl_internal_format, width, height); |
| 352 | break; | 354 | break; |
| 353 | case GL_TEXTURE_3D: | 355 | case GL_TEXTURE_3D: |
| 354 | glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, depth); | 356 | glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, depth); |
| 355 | break; | 357 | break; |
| 356 | case GL_TEXTURE_BUFFER: | 358 | case GL_TEXTURE_BUFFER: |
| 357 | UNREACHABLE(); | 359 | UNREACHABLE(); |
| @@ -397,9 +399,6 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form | |||
| 397 | return GL_R32UI; | 399 | return GL_R32UI; |
| 398 | } | 400 | } |
| 399 | 401 | ||
| 400 | [[nodiscard]] u32 NextPow2(u32 value) { | ||
| 401 | return 1U << (32U - std::countl_zero(value - 1U)); | ||
| 402 | } | ||
| 403 | } // Anonymous namespace | 402 | } // Anonymous namespace |
| 404 | 403 | ||
| 405 | ImageBufferMap::~ImageBufferMap() { | 404 | ImageBufferMap::~ImageBufferMap() { |
| @@ -526,8 +525,8 @@ void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image, | |||
| 526 | } | 525 | } |
| 527 | } | 526 | } |
| 528 | 527 | ||
| 529 | void TextureCacheRuntime::ConvertImage(Image& dst, Image& src, | 528 | void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, |
| 530 | std::span<const VideoCommon::ImageCopy> copies) { | 529 | std::span<const VideoCommon::ImageCopy> copies) { |
| 531 | LOG_DEBUG(Render_OpenGL, "Converting {} to {}", src.info.format, dst.info.format); | 530 | LOG_DEBUG(Render_OpenGL, "Converting {} to {}", src.info.format, dst.info.format); |
| 532 | format_conversion_pass.ConvertImage(dst, src, copies); | 531 | format_conversion_pass.ConvertImage(dst, src, copies); |
| 533 | } | 532 | } |
| @@ -696,7 +695,9 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_, | |||
| 696 | gl_format = tuple.format; | 695 | gl_format = tuple.format; |
| 697 | gl_type = tuple.type; | 696 | gl_type = tuple.type; |
| 698 | } | 697 | } |
| 699 | texture = MakeImage(info, gl_internal_format); | 698 | const int max_host_mip_levels = std::bit_width(info.size.width); |
| 699 | gl_num_levels = std::min(info.resources.levels, max_host_mip_levels); | ||
| 700 | texture = MakeImage(info, gl_internal_format, gl_num_levels); | ||
| 700 | current_texture = texture.handle; | 701 | current_texture = texture.handle; |
| 701 | if (runtime->device.HasDebuggingToolAttached()) { | 702 | if (runtime->device.HasDebuggingToolAttached()) { |
| 702 | const std::string name = VideoCommon::Name(*this); | 703 | const std::string name = VideoCommon::Name(*this); |
| @@ -724,6 +725,9 @@ void Image::UploadMemory(const ImageBufferMap& map, | |||
| 724 | u32 current_image_height = std::numeric_limits<u32>::max(); | 725 | u32 current_image_height = std::numeric_limits<u32>::max(); |
| 725 | 726 | ||
| 726 | for (const VideoCommon::BufferImageCopy& copy : copies) { | 727 | for (const VideoCommon::BufferImageCopy& copy : copies) { |
| 728 | if (copy.image_subresource.base_level >= gl_num_levels) { | ||
| 729 | continue; | ||
| 730 | } | ||
| 727 | if (current_row_length != copy.buffer_row_length) { | 731 | if (current_row_length != copy.buffer_row_length) { |
| 728 | current_row_length = copy.buffer_row_length; | 732 | current_row_length = copy.buffer_row_length; |
| 729 | glPixelStorei(GL_UNPACK_ROW_LENGTH, current_row_length); | 733 | glPixelStorei(GL_UNPACK_ROW_LENGTH, current_row_length); |
| @@ -753,6 +757,9 @@ void Image::DownloadMemory(ImageBufferMap& map, | |||
| 753 | u32 current_image_height = std::numeric_limits<u32>::max(); | 757 | u32 current_image_height = std::numeric_limits<u32>::max(); |
| 754 | 758 | ||
| 755 | for (const VideoCommon::BufferImageCopy& copy : copies) { | 759 | for (const VideoCommon::BufferImageCopy& copy : copies) { |
| 760 | if (copy.image_subresource.base_level >= gl_num_levels) { | ||
| 761 | continue; | ||
| 762 | } | ||
| 756 | if (current_row_length != copy.buffer_row_length) { | 763 | if (current_row_length != copy.buffer_row_length) { |
| 757 | current_row_length = copy.buffer_row_length; | 764 | current_row_length = copy.buffer_row_length; |
| 758 | glPixelStorei(GL_PACK_ROW_LENGTH, current_row_length); | 765 | glPixelStorei(GL_PACK_ROW_LENGTH, current_row_length); |
| @@ -792,7 +799,7 @@ GLuint Image::StorageHandle() noexcept { | |||
| 792 | } | 799 | } |
| 793 | store_view.Create(); | 800 | store_view.Create(); |
| 794 | glTextureView(store_view.handle, ImageTarget(info), current_texture, GL_RGBA8, 0, | 801 | glTextureView(store_view.handle, ImageTarget(info), current_texture, GL_RGBA8, 0, |
| 795 | info.resources.levels, 0, info.resources.layers); | 802 | gl_num_levels, 0, info.resources.layers); |
| 796 | return store_view.handle; | 803 | return store_view.handle; |
| 797 | default: | 804 | default: |
| 798 | return current_texture; | 805 | return current_texture; |
| @@ -907,6 +914,8 @@ void Image::Scale(bool up_scale) { | |||
| 907 | return GL_COLOR_ATTACHMENT0; | 914 | return GL_COLOR_ATTACHMENT0; |
| 908 | case SurfaceType::Depth: | 915 | case SurfaceType::Depth: |
| 909 | return GL_DEPTH_ATTACHMENT; | 916 | return GL_DEPTH_ATTACHMENT; |
| 917 | case SurfaceType::Stencil: | ||
| 918 | return GL_STENCIL_ATTACHMENT; | ||
| 910 | case SurfaceType::DepthStencil: | 919 | case SurfaceType::DepthStencil: |
| 911 | return GL_DEPTH_STENCIL_ATTACHMENT; | 920 | return GL_DEPTH_STENCIL_ATTACHMENT; |
| 912 | default: | 921 | default: |
| @@ -920,8 +929,10 @@ void Image::Scale(bool up_scale) { | |||
| 920 | return GL_COLOR_BUFFER_BIT; | 929 | return GL_COLOR_BUFFER_BIT; |
| 921 | case SurfaceType::Depth: | 930 | case SurfaceType::Depth: |
| 922 | return GL_DEPTH_BUFFER_BIT; | 931 | return GL_DEPTH_BUFFER_BIT; |
| 932 | case SurfaceType::Stencil: | ||
| 933 | return GL_STENCIL_BUFFER_BIT; | ||
| 923 | case SurfaceType::DepthStencil: | 934 | case SurfaceType::DepthStencil: |
| 924 | return GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; | 935 | return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; |
| 925 | default: | 936 | default: |
| 926 | UNREACHABLE(); | 937 | UNREACHABLE(); |
| 927 | return GL_COLOR_BUFFER_BIT; | 938 | return GL_COLOR_BUFFER_BIT; |
| @@ -933,8 +944,10 @@ void Image::Scale(bool up_scale) { | |||
| 933 | return 0; | 944 | return 0; |
| 934 | case SurfaceType::Depth: | 945 | case SurfaceType::Depth: |
| 935 | return 1; | 946 | return 1; |
| 936 | case SurfaceType::DepthStencil: | 947 | case SurfaceType::Stencil: |
| 937 | return 2; | 948 | return 2; |
| 949 | case SurfaceType::DepthStencil: | ||
| 950 | return 3; | ||
| 938 | default: | 951 | default: |
| 939 | UNREACHABLE(); | 952 | UNREACHABLE(); |
| 940 | return 0; | 953 | return 0; |
| @@ -956,7 +969,7 @@ void Image::Scale(bool up_scale) { | |||
| 956 | auto dst_info = info; | 969 | auto dst_info = info; |
| 957 | dst_info.size.width = scaled_width; | 970 | dst_info.size.width = scaled_width; |
| 958 | dst_info.size.height = scaled_height; | 971 | dst_info.size.height = scaled_height; |
| 959 | upscaled_backup = MakeImage(dst_info, gl_internal_format); | 972 | upscaled_backup = MakeImage(dst_info, gl_internal_format, gl_num_levels); |
| 960 | } | 973 | } |
| 961 | const u32 src_width = up_scale ? original_width : scaled_width; | 974 | const u32 src_width = up_scale ? original_width : scaled_width; |
| 962 | const u32 src_height = up_scale ? original_height : scaled_height; | 975 | const u32 src_height = up_scale ? original_height : scaled_height; |
| @@ -1264,10 +1277,20 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM | |||
| 1264 | } | 1277 | } |
| 1265 | 1278 | ||
| 1266 | if (const ImageView* const image_view = depth_buffer; image_view) { | 1279 | if (const ImageView* const image_view = depth_buffer; image_view) { |
| 1267 | if (GetFormatType(image_view->format) == SurfaceType::DepthStencil) { | 1280 | switch (GetFormatType(image_view->format)) { |
| 1281 | case SurfaceType::Depth: | ||
| 1282 | buffer_bits |= GL_DEPTH_BUFFER_BIT; | ||
| 1283 | break; | ||
| 1284 | case SurfaceType::Stencil: | ||
| 1285 | buffer_bits |= GL_STENCIL_BUFFER_BIT; | ||
| 1286 | break; | ||
| 1287 | case SurfaceType::DepthStencil: | ||
| 1268 | buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; | 1288 | buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; |
| 1269 | } else { | 1289 | break; |
| 1290 | default: | ||
| 1291 | UNREACHABLE(); | ||
| 1270 | buffer_bits |= GL_DEPTH_BUFFER_BIT; | 1292 | buffer_bits |= GL_DEPTH_BUFFER_BIT; |
| 1293 | break; | ||
| 1271 | } | 1294 | } |
| 1272 | const GLenum attachment = AttachmentType(image_view->format); | 1295 | const GLenum attachment = AttachmentType(image_view->format); |
| 1273 | AttachTexture(handle, attachment, image_view); | 1296 | AttachTexture(handle, attachment, image_view); |
| @@ -1308,7 +1331,7 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image, | |||
| 1308 | const u32 copy_size = region.width * region.height * region.depth * img_bpp; | 1331 | const u32 copy_size = region.width * region.height * region.depth * img_bpp; |
| 1309 | if (pbo_size < copy_size) { | 1332 | if (pbo_size < copy_size) { |
| 1310 | intermediate_pbo.Create(); | 1333 | intermediate_pbo.Create(); |
| 1311 | pbo_size = NextPow2(copy_size); | 1334 | pbo_size = Common::NextPow2(copy_size); |
| 1312 | glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY); | 1335 | glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY); |
| 1313 | } | 1336 | } |
| 1314 | // Copy from source to PBO | 1337 | // Copy from source to PBO |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 578f8d523..37d5e6a6b 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -84,9 +84,13 @@ public: | |||
| 84 | 84 | ||
| 85 | u64 GetDeviceLocalMemory() const; | 85 | u64 GetDeviceLocalMemory() const; |
| 86 | 86 | ||
| 87 | bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) { | ||
| 88 | return true; | ||
| 89 | } | ||
| 90 | |||
| 87 | void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); | 91 | void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); |
| 88 | 92 | ||
| 89 | void ConvertImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); | 93 | void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); |
| 90 | 94 | ||
| 91 | void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled) { | 95 | void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled) { |
| 92 | UNIMPLEMENTED(); | 96 | UNIMPLEMENTED(); |
| @@ -164,8 +168,8 @@ private: | |||
| 164 | 168 | ||
| 165 | std::array<GLuint, Shader::NUM_TEXTURE_TYPES> null_image_views{}; | 169 | std::array<GLuint, Shader::NUM_TEXTURE_TYPES> null_image_views{}; |
| 166 | 170 | ||
| 167 | std::array<OGLFramebuffer, 3> rescale_draw_fbos; | 171 | std::array<OGLFramebuffer, 4> rescale_draw_fbos; |
| 168 | std::array<OGLFramebuffer, 3> rescale_read_fbos; | 172 | std::array<OGLFramebuffer, 4> rescale_read_fbos; |
| 169 | const Settings::ResolutionScalingInfo& resolution; | 173 | const Settings::ResolutionScalingInfo& resolution; |
| 170 | }; | 174 | }; |
| 171 | 175 | ||
| @@ -221,6 +225,7 @@ private: | |||
| 221 | GLenum gl_internal_format = GL_NONE; | 225 | GLenum gl_internal_format = GL_NONE; |
| 222 | GLenum gl_format = GL_NONE; | 226 | GLenum gl_format = GL_NONE; |
| 223 | GLenum gl_type = GL_NONE; | 227 | GLenum gl_type = GL_NONE; |
| 228 | GLsizei gl_num_levels{}; | ||
| 224 | TextureCacheRuntime* runtime{}; | 229 | TextureCacheRuntime* runtime{}; |
| 225 | GLuint current_texture{}; | 230 | GLuint current_texture{}; |
| 226 | }; | 231 | }; |
| @@ -338,7 +343,6 @@ struct TextureCacheParams { | |||
| 338 | static constexpr bool FRAMEBUFFER_BLITS = true; | 343 | static constexpr bool FRAMEBUFFER_BLITS = true; |
| 339 | static constexpr bool HAS_EMULATED_COPIES = true; | 344 | static constexpr bool HAS_EMULATED_COPIES = true; |
| 340 | static constexpr bool HAS_DEVICE_MEMORY_INFO = true; | 345 | static constexpr bool HAS_DEVICE_MEMORY_INFO = true; |
| 341 | static constexpr bool HAS_PIXEL_FORMAT_CONVERSIONS = true; | ||
| 342 | 346 | ||
| 343 | using Runtime = OpenGL::TextureCacheRuntime; | 347 | using Runtime = OpenGL::TextureCacheRuntime; |
| 344 | using Image = OpenGL::Image; | 348 | using Image = OpenGL::Image; |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 39158aa3e..daba42ed9 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -108,6 +108,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB | |||
| 108 | {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT | 108 | {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT |
| 109 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT | 109 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT |
| 110 | {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM | 110 | {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM |
| 111 | {GL_STENCIL_INDEX8, GL_STENCIL, GL_UNSIGNED_BYTE}, // S8_UINT | ||
| 111 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT | 112 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT |
| 112 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM | 113 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM |
| 113 | {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, | 114 | {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, |
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index b3884a4f5..9a38b6b34 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | 6 | ||
| 7 | #include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" | ||
| 8 | #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" | ||
| 7 | #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" | 9 | #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" |
| 8 | #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" | 10 | #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" |
| 9 | #include "video_core/host_shaders/full_screen_triangle_vert_spv.h" | 11 | #include "video_core/host_shaders/full_screen_triangle_vert_spv.h" |
| @@ -354,6 +356,8 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_, | |||
| 354 | blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), | 356 | blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), |
| 355 | convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), | 357 | convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), |
| 356 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), | 358 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), |
| 359 | convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), | ||
| 360 | convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), | ||
| 357 | linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), | 361 | linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), |
| 358 | nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { | 362 | nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { |
| 359 | if (device.IsExtShaderStencilExportSupported()) { | 363 | if (device.IsExtShaderStencilExportSupported()) { |
| @@ -448,6 +452,22 @@ void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer, | |||
| 448 | Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift); | 452 | Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift); |
| 449 | } | 453 | } |
| 450 | 454 | ||
| 455 | void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, | ||
| 456 | ImageView& src_image_view, u32 up_scale, u32 down_shift) { | ||
| 457 | ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(), | ||
| 458 | convert_abgr8_to_d24s8_frag, true); | ||
| 459 | ConvertColor(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale, | ||
| 460 | down_shift); | ||
| 461 | } | ||
| 462 | |||
| 463 | void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, | ||
| 464 | ImageView& src_image_view, u32 up_scale, u32 down_shift) { | ||
| 465 | ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(), | ||
| 466 | convert_d24s8_to_abgr8_frag, false); | ||
| 467 | ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view, up_scale, | ||
| 468 | down_shift); | ||
| 469 | } | ||
| 470 | |||
| 451 | void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, | 471 | void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, |
| 452 | const ImageView& src_image_view, u32 up_scale, u32 down_shift) { | 472 | const ImageView& src_image_view, u32 up_scale, u32 down_shift) { |
| 453 | const VkPipelineLayout layout = *one_texture_pipeline_layout; | 473 | const VkPipelineLayout layout = *one_texture_pipeline_layout; |
| @@ -495,6 +515,101 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb | |||
| 495 | scheduler.InvalidateState(); | 515 | scheduler.InvalidateState(); |
| 496 | } | 516 | } |
| 497 | 517 | ||
| 518 | void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer, | ||
| 519 | ImageView& src_image_view, u32 up_scale, u32 down_shift) { | ||
| 520 | const VkPipelineLayout layout = *one_texture_pipeline_layout; | ||
| 521 | const VkImageView src_view = src_image_view.ColorView(); | ||
| 522 | const VkSampler sampler = *nearest_sampler; | ||
| 523 | const VkExtent2D extent{ | ||
| 524 | .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U), | ||
| 525 | .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U), | ||
| 526 | }; | ||
| 527 | scheduler.RequestRenderpass(dst_framebuffer); | ||
| 528 | scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift, | ||
| 529 | this](vk::CommandBuffer cmdbuf) { | ||
| 530 | const VkOffset2D offset{ | ||
| 531 | .x = 0, | ||
| 532 | .y = 0, | ||
| 533 | }; | ||
| 534 | const VkViewport viewport{ | ||
| 535 | .x = 0.0f, | ||
| 536 | .y = 0.0f, | ||
| 537 | .width = static_cast<float>(extent.width), | ||
| 538 | .height = static_cast<float>(extent.height), | ||
| 539 | .minDepth = 0.0f, | ||
| 540 | .maxDepth = 0.0f, | ||
| 541 | }; | ||
| 542 | const VkRect2D scissor{ | ||
| 543 | .offset = offset, | ||
| 544 | .extent = extent, | ||
| 545 | }; | ||
| 546 | const PushConstants push_constants{ | ||
| 547 | .tex_scale = {viewport.width, viewport.height}, | ||
| 548 | .tex_offset = {0.0f, 0.0f}, | ||
| 549 | }; | ||
| 550 | const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); | ||
| 551 | UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view); | ||
| 552 | |||
| 553 | // TODO: Barriers | ||
| 554 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); | ||
| 555 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, | ||
| 556 | nullptr); | ||
| 557 | cmdbuf.SetViewport(0, viewport); | ||
| 558 | cmdbuf.SetScissor(0, scissor); | ||
| 559 | cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants); | ||
| 560 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 561 | }); | ||
| 562 | scheduler.InvalidateState(); | ||
| 563 | } | ||
| 564 | |||
| 565 | void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer, | ||
| 566 | ImageView& src_image_view, u32 up_scale, u32 down_shift) { | ||
| 567 | const VkPipelineLayout layout = *two_textures_pipeline_layout; | ||
| 568 | const VkImageView src_depth_view = src_image_view.DepthView(); | ||
| 569 | const VkImageView src_stencil_view = src_image_view.StencilView(); | ||
| 570 | const VkSampler sampler = *nearest_sampler; | ||
| 571 | const VkExtent2D extent{ | ||
| 572 | .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U), | ||
| 573 | .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U), | ||
| 574 | }; | ||
| 575 | scheduler.RequestRenderpass(dst_framebuffer); | ||
| 576 | scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent, up_scale, | ||
| 577 | down_shift, this](vk::CommandBuffer cmdbuf) { | ||
| 578 | const VkOffset2D offset{ | ||
| 579 | .x = 0, | ||
| 580 | .y = 0, | ||
| 581 | }; | ||
| 582 | const VkViewport viewport{ | ||
| 583 | .x = 0.0f, | ||
| 584 | .y = 0.0f, | ||
| 585 | .width = static_cast<float>(extent.width), | ||
| 586 | .height = static_cast<float>(extent.height), | ||
| 587 | .minDepth = 0.0f, | ||
| 588 | .maxDepth = 0.0f, | ||
| 589 | }; | ||
| 590 | const VkRect2D scissor{ | ||
| 591 | .offset = offset, | ||
| 592 | .extent = extent, | ||
| 593 | }; | ||
| 594 | const PushConstants push_constants{ | ||
| 595 | .tex_scale = {viewport.width, viewport.height}, | ||
| 596 | .tex_offset = {0.0f, 0.0f}, | ||
| 597 | }; | ||
| 598 | const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit(); | ||
| 599 | UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view, | ||
| 600 | src_stencil_view); | ||
| 601 | // TODO: Barriers | ||
| 602 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); | ||
| 603 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, | ||
| 604 | nullptr); | ||
| 605 | cmdbuf.SetViewport(0, viewport); | ||
| 606 | cmdbuf.SetScissor(0, scissor); | ||
| 607 | cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants); | ||
| 608 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 609 | }); | ||
| 610 | scheduler.InvalidateState(); | ||
| 611 | } | ||
| 612 | |||
| 498 | VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key) { | 613 | VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key) { |
| 499 | const auto it = std::ranges::find(blit_color_keys, key); | 614 | const auto it = std::ranges::find(blit_color_keys, key); |
| 500 | if (it != blit_color_keys.end()) { | 615 | if (it != blit_color_keys.end()) { |
| @@ -636,4 +751,44 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend | |||
| 636 | }); | 751 | }); |
| 637 | } | 752 | } |
| 638 | 753 | ||
| 754 | void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass, | ||
| 755 | vk::ShaderModule& module, bool is_target_depth, | ||
| 756 | bool single_texture) { | ||
| 757 | if (pipeline) { | ||
| 758 | return; | ||
| 759 | } | ||
| 760 | const std::array stages = MakeStages(*full_screen_vert, *module); | ||
| 761 | pipeline = device.GetLogical().CreateGraphicsPipeline({ | ||
| 762 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||
| 763 | .pNext = nullptr, | ||
| 764 | .flags = 0, | ||
| 765 | .stageCount = static_cast<u32>(stages.size()), | ||
| 766 | .pStages = stages.data(), | ||
| 767 | .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | ||
| 768 | .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | ||
| 769 | .pTessellationState = nullptr, | ||
| 770 | .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, | ||
| 771 | .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | ||
| 772 | .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | ||
| 773 | .pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr, | ||
| 774 | .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, | ||
| 775 | .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, | ||
| 776 | .layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout, | ||
| 777 | .renderPass = renderpass, | ||
| 778 | .subpass = 0, | ||
| 779 | .basePipelineHandle = VK_NULL_HANDLE, | ||
| 780 | .basePipelineIndex = 0, | ||
| 781 | }); | ||
| 782 | } | ||
| 783 | |||
| 784 | void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, | ||
| 785 | vk::ShaderModule& module, bool single_texture) { | ||
| 786 | ConvertPipelineEx(pipeline, renderpass, module, false, single_texture); | ||
| 787 | } | ||
| 788 | |||
| 789 | void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, | ||
| 790 | vk::ShaderModule& module, bool single_texture) { | ||
| 791 | ConvertPipelineEx(pipeline, renderpass, module, true, single_texture); | ||
| 792 | } | ||
| 793 | |||
| 639 | } // namespace Vulkan | 794 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index d77f76678..b1a717090 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h | |||
| @@ -56,10 +56,22 @@ public: | |||
| 56 | void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, | 56 | void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, |
| 57 | u32 up_scale, u32 down_shift); | 57 | u32 up_scale, u32 down_shift); |
| 58 | 58 | ||
| 59 | void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, ImageView& src_image_view, | ||
| 60 | u32 up_scale, u32 down_shift); | ||
| 61 | |||
| 62 | void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view, | ||
| 63 | u32 up_scale, u32 down_shift); | ||
| 64 | |||
| 59 | private: | 65 | private: |
| 60 | void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, | 66 | void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, |
| 61 | const ImageView& src_image_view, u32 up_scale, u32 down_shift); | 67 | const ImageView& src_image_view, u32 up_scale, u32 down_shift); |
| 62 | 68 | ||
| 69 | void ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer, | ||
| 70 | ImageView& src_image_view, u32 up_scale, u32 down_shift); | ||
| 71 | |||
| 72 | void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer, | ||
| 73 | ImageView& src_image_view, u32 up_scale, u32 down_shift); | ||
| 74 | |||
| 63 | [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key); | 75 | [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key); |
| 64 | 76 | ||
| 65 | [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); | 77 | [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); |
| @@ -68,6 +80,15 @@ private: | |||
| 68 | 80 | ||
| 69 | void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); | 81 | void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); |
| 70 | 82 | ||
| 83 | void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass, | ||
| 84 | vk::ShaderModule& module, bool is_target_depth, bool single_texture); | ||
| 85 | |||
| 86 | void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, | ||
| 87 | vk::ShaderModule& module, bool single_texture); | ||
| 88 | |||
| 89 | void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, | ||
| 90 | vk::ShaderModule& module, bool single_texture); | ||
| 91 | |||
| 71 | const Device& device; | 92 | const Device& device; |
| 72 | VKScheduler& scheduler; | 93 | VKScheduler& scheduler; |
| 73 | StateTracker& state_tracker; | 94 | StateTracker& state_tracker; |
| @@ -83,6 +104,8 @@ private: | |||
| 83 | vk::ShaderModule blit_depth_stencil_frag; | 104 | vk::ShaderModule blit_depth_stencil_frag; |
| 84 | vk::ShaderModule convert_depth_to_float_frag; | 105 | vk::ShaderModule convert_depth_to_float_frag; |
| 85 | vk::ShaderModule convert_float_to_depth_frag; | 106 | vk::ShaderModule convert_float_to_depth_frag; |
| 107 | vk::ShaderModule convert_abgr8_to_d24s8_frag; | ||
| 108 | vk::ShaderModule convert_d24s8_to_abgr8_frag; | ||
| 86 | vk::Sampler linear_sampler; | 109 | vk::Sampler linear_sampler; |
| 87 | vk::Sampler nearest_sampler; | 110 | vk::Sampler nearest_sampler; |
| 88 | 111 | ||
| @@ -94,6 +117,8 @@ private: | |||
| 94 | vk::Pipeline convert_r32_to_d32_pipeline; | 117 | vk::Pipeline convert_r32_to_d32_pipeline; |
| 95 | vk::Pipeline convert_d16_to_r16_pipeline; | 118 | vk::Pipeline convert_d16_to_r16_pipeline; |
| 96 | vk::Pipeline convert_r16_to_d16_pipeline; | 119 | vk::Pipeline convert_r16_to_d16_pipeline; |
| 120 | vk::Pipeline convert_abgr8_to_d24s8_pipeline; | ||
| 121 | vk::Pipeline convert_d24s8_to_abgr8_pipeline; | ||
| 97 | }; | 122 | }; |
| 98 | 123 | ||
| 99 | } // namespace Vulkan | 124 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 68a23b602..751e4792b 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -162,7 +162,7 @@ struct FormatTuple { | |||
| 162 | {VK_FORMAT_UNDEFINED}, // R16_SINT | 162 | {VK_FORMAT_UNDEFINED}, // R16_SINT |
| 163 | {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM | 163 | {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM |
| 164 | {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT | 164 | {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT |
| 165 | {VK_FORMAT_UNDEFINED}, // R16G16_UINT | 165 | {VK_FORMAT_R16G16_UINT, Attachable | Storage}, // R16G16_UINT |
| 166 | {VK_FORMAT_R16G16_SINT, Attachable | Storage}, // R16G16_SINT | 166 | {VK_FORMAT_R16G16_SINT, Attachable | Storage}, // R16G16_SINT |
| 167 | {VK_FORMAT_R16G16_SNORM, Attachable | Storage}, // R16G16_SNORM | 167 | {VK_FORMAT_R16G16_SNORM, Attachable | Storage}, // R16G16_SNORM |
| 168 | {VK_FORMAT_UNDEFINED}, // R32G32B32_FLOAT | 168 | {VK_FORMAT_UNDEFINED}, // R32G32B32_FLOAT |
| @@ -176,8 +176,8 @@ struct FormatTuple { | |||
| 176 | {VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT | 176 | {VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT |
| 177 | {VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT | 177 | {VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT |
| 178 | {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM | 178 | {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM |
| 179 | {VK_FORMAT_UNDEFINED}, // ASTC_2D_8X5_UNORM | 179 | {VK_FORMAT_ASTC_8x5_UNORM_BLOCK}, // ASTC_2D_8X5_UNORM |
| 180 | {VK_FORMAT_UNDEFINED}, // ASTC_2D_5X4_UNORM | 180 | {VK_FORMAT_ASTC_5x4_UNORM_BLOCK}, // ASTC_2D_5X4_UNORM |
| 181 | {VK_FORMAT_B8G8R8A8_SRGB, Attachable}, // B8G8R8A8_SRGB | 181 | {VK_FORMAT_B8G8R8A8_SRGB, Attachable}, // B8G8R8A8_SRGB |
| 182 | {VK_FORMAT_BC1_RGBA_SRGB_BLOCK}, // BC1_RGBA_SRGB | 182 | {VK_FORMAT_BC1_RGBA_SRGB_BLOCK}, // BC1_RGBA_SRGB |
| 183 | {VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB | 183 | {VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB |
| @@ -208,6 +208,9 @@ struct FormatTuple { | |||
| 208 | {VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT | 208 | {VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT |
| 209 | {VK_FORMAT_D16_UNORM, Attachable}, // D16_UNORM | 209 | {VK_FORMAT_D16_UNORM, Attachable}, // D16_UNORM |
| 210 | 210 | ||
| 211 | // Stencil formats | ||
| 212 | {VK_FORMAT_S8_UINT, Attachable}, // S8_UINT | ||
| 213 | |||
| 211 | // DepthStencil formats | 214 | // DepthStencil formats |
| 212 | {VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // D24_UNORM_S8_UINT | 215 | {VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // D24_UNORM_S8_UINT |
| 213 | {VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // S8_UINT_D24_UNORM (emulated) | 216 | {VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // S8_UINT_D24_UNORM (emulated) |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 407fd2a15..197cba8e3 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include "common/bit_cast.h" | 10 | #include "common/bit_cast.h" |
| 11 | #include "common/bit_util.h" | ||
| 11 | #include "common/settings.h" | 12 | #include "common/settings.h" |
| 12 | 13 | ||
| 13 | #include "video_core/engines/fermi_2d.h" | 14 | #include "video_core/engines/fermi_2d.h" |
| @@ -102,6 +103,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 102 | usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | 103 | usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| 103 | break; | 104 | break; |
| 104 | case VideoCore::Surface::SurfaceType::Depth: | 105 | case VideoCore::Surface::SurfaceType::Depth: |
| 106 | case VideoCore::Surface::SurfaceType::Stencil: | ||
| 105 | case VideoCore::Surface::SurfaceType::DepthStencil: | 107 | case VideoCore::Surface::SurfaceType::DepthStencil: |
| 106 | usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; | 108 | usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; |
| 107 | break; | 109 | break; |
| @@ -173,6 +175,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 173 | return VK_IMAGE_ASPECT_COLOR_BIT; | 175 | return VK_IMAGE_ASPECT_COLOR_BIT; |
| 174 | case VideoCore::Surface::SurfaceType::Depth: | 176 | case VideoCore::Surface::SurfaceType::Depth: |
| 175 | return VK_IMAGE_ASPECT_DEPTH_BIT; | 177 | return VK_IMAGE_ASPECT_DEPTH_BIT; |
| 178 | case VideoCore::Surface::SurfaceType::Stencil: | ||
| 179 | return VK_IMAGE_ASPECT_STENCIL_BIT; | ||
| 176 | case VideoCore::Surface::SurfaceType::DepthStencil: | 180 | case VideoCore::Surface::SurfaceType::DepthStencil: |
| 177 | return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; | 181 | return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; |
| 178 | default: | 182 | default: |
| @@ -195,6 +199,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 195 | case PixelFormat::D16_UNORM: | 199 | case PixelFormat::D16_UNORM: |
| 196 | case PixelFormat::D32_FLOAT: | 200 | case PixelFormat::D32_FLOAT: |
| 197 | return VK_IMAGE_ASPECT_DEPTH_BIT; | 201 | return VK_IMAGE_ASPECT_DEPTH_BIT; |
| 202 | case PixelFormat::S8_UINT: | ||
| 203 | return VK_IMAGE_ASPECT_STENCIL_BIT; | ||
| 198 | default: | 204 | default: |
| 199 | return VK_IMAGE_ASPECT_COLOR_BIT; | 205 | return VK_IMAGE_ASPECT_COLOR_BIT; |
| 200 | } | 206 | } |
| @@ -308,6 +314,19 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 308 | }; | 314 | }; |
| 309 | } | 315 | } |
| 310 | 316 | ||
| 317 | [[nodiscard]] VkBufferImageCopy MakeBufferImageCopy(const VideoCommon::ImageCopy& copy, bool is_src, | ||
| 318 | VkImageAspectFlags aspect_mask) noexcept { | ||
| 319 | return VkBufferImageCopy{ | ||
| 320 | .bufferOffset = 0, | ||
| 321 | .bufferRowLength = 0, | ||
| 322 | .bufferImageHeight = 0, | ||
| 323 | .imageSubresource = MakeImageSubresourceLayers( | ||
| 324 | is_src ? copy.src_subresource : copy.dst_subresource, aspect_mask), | ||
| 325 | .imageOffset = MakeOffset3D(is_src ? copy.src_offset : copy.dst_offset), | ||
| 326 | .imageExtent = MakeExtent3D(copy.extent), | ||
| 327 | }; | ||
| 328 | } | ||
| 329 | |||
| 311 | [[maybe_unused]] [[nodiscard]] std::vector<VkBufferCopy> TransformBufferCopies( | 330 | [[maybe_unused]] [[nodiscard]] std::vector<VkBufferCopy> TransformBufferCopies( |
| 312 | std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { | 331 | std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { |
| 313 | std::vector<VkBufferCopy> result(copies.size()); | 332 | std::vector<VkBufferCopy> result(copies.size()); |
| @@ -754,6 +773,173 @@ StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size) { | |||
| 754 | return staging_buffer_pool.Request(size, MemoryUsage::Download); | 773 | return staging_buffer_pool.Request(size, MemoryUsage::Download); |
| 755 | } | 774 | } |
| 756 | 775 | ||
| 776 | bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { | ||
| 777 | if (VideoCore::Surface::GetFormatType(dst.info.format) == | ||
| 778 | VideoCore::Surface::SurfaceType::DepthStencil && | ||
| 779 | !device.IsExtShaderStencilExportSupported()) { | ||
| 780 | return true; | ||
| 781 | } | ||
| 782 | if (VideoCore::Surface::GetFormatType(src.info.format) == | ||
| 783 | VideoCore::Surface::SurfaceType::DepthStencil && | ||
| 784 | !device.IsExtShaderStencilExportSupported()) { | ||
| 785 | return true; | ||
| 786 | } | ||
| 787 | if (dst.info.format == PixelFormat::D32_FLOAT_S8_UINT || | ||
| 788 | src.info.format == PixelFormat::D32_FLOAT_S8_UINT) { | ||
| 789 | return true; | ||
| 790 | } | ||
| 791 | return false; | ||
| 792 | } | ||
| 793 | |||
| 794 | VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { | ||
| 795 | const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL); | ||
| 796 | if (buffer_commits[level]) { | ||
| 797 | return *buffers[level]; | ||
| 798 | } | ||
| 799 | const auto new_size = Common::NextPow2(needed_size); | ||
| 800 | static constexpr VkBufferUsageFlags flags = | ||
| 801 | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | | ||
| 802 | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; | ||
| 803 | buffers[level] = device.GetLogical().CreateBuffer({ | ||
| 804 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||
| 805 | .pNext = nullptr, | ||
| 806 | .flags = 0, | ||
| 807 | .size = new_size, | ||
| 808 | .usage = flags, | ||
| 809 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 810 | .queueFamilyIndexCount = 0, | ||
| 811 | .pQueueFamilyIndices = nullptr, | ||
| 812 | }); | ||
| 813 | buffer_commits[level] = std::make_unique<MemoryCommit>( | ||
| 814 | memory_allocator.Commit(buffers[level], MemoryUsage::DeviceLocal)); | ||
| 815 | return *buffers[level]; | ||
| 816 | } | ||
| 817 | |||
| 818 | void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, | ||
| 819 | std::span<const VideoCommon::ImageCopy> copies) { | ||
| 820 | std::vector<VkBufferImageCopy> vk_in_copies(copies.size()); | ||
| 821 | std::vector<VkBufferImageCopy> vk_out_copies(copies.size()); | ||
| 822 | const VkImageAspectFlags src_aspect_mask = src.AspectMask(); | ||
| 823 | const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); | ||
| 824 | |||
| 825 | std::ranges::transform(copies, vk_in_copies.begin(), [src_aspect_mask](const auto& copy) { | ||
| 826 | return MakeBufferImageCopy(copy, true, src_aspect_mask); | ||
| 827 | }); | ||
| 828 | std::ranges::transform(copies, vk_out_copies.begin(), [dst_aspect_mask](const auto& copy) { | ||
| 829 | return MakeBufferImageCopy(copy, false, dst_aspect_mask); | ||
| 830 | }); | ||
| 831 | const u32 img_bpp = BytesPerBlock(src.info.format); | ||
| 832 | size_t total_size = 0; | ||
| 833 | for (const auto& copy : copies) { | ||
| 834 | total_size += copy.extent.width * copy.extent.height * copy.extent.depth * img_bpp; | ||
| 835 | } | ||
| 836 | const VkBuffer copy_buffer = GetTemporaryBuffer(total_size); | ||
| 837 | const VkImage dst_image = dst.Handle(); | ||
| 838 | const VkImage src_image = src.Handle(); | ||
| 839 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 840 | scheduler.Record([dst_image, src_image, copy_buffer, src_aspect_mask, dst_aspect_mask, | ||
| 841 | vk_in_copies, vk_out_copies](vk::CommandBuffer cmdbuf) { | ||
| 842 | RangedBarrierRange dst_range; | ||
| 843 | RangedBarrierRange src_range; | ||
| 844 | for (const VkBufferImageCopy& copy : vk_in_copies) { | ||
| 845 | src_range.AddLayers(copy.imageSubresource); | ||
| 846 | } | ||
| 847 | for (const VkBufferImageCopy& copy : vk_out_copies) { | ||
| 848 | dst_range.AddLayers(copy.imageSubresource); | ||
| 849 | } | ||
| 850 | static constexpr VkMemoryBarrier READ_BARRIER{ | ||
| 851 | .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, | ||
| 852 | .pNext = nullptr, | ||
| 853 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 854 | .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, | ||
| 855 | }; | ||
| 856 | static constexpr VkMemoryBarrier WRITE_BARRIER{ | ||
| 857 | .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, | ||
| 858 | .pNext = nullptr, | ||
| 859 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | ||
| 860 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 861 | }; | ||
| 862 | const std::array pre_barriers{ | ||
| 863 | VkImageMemoryBarrier{ | ||
| 864 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 865 | .pNext = nullptr, | ||
| 866 | .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | | ||
| 867 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | | ||
| 868 | VK_ACCESS_TRANSFER_WRITE_BIT, | ||
| 869 | .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, | ||
| 870 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 871 | .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 872 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 873 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 874 | .image = src_image, | ||
| 875 | .subresourceRange = src_range.SubresourceRange(src_aspect_mask), | ||
| 876 | }, | ||
| 877 | }; | ||
| 878 | const std::array middle_in_barrier{ | ||
| 879 | VkImageMemoryBarrier{ | ||
| 880 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 881 | .pNext = nullptr, | ||
| 882 | .srcAccessMask = 0, | ||
| 883 | .dstAccessMask = 0, | ||
| 884 | .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 885 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 886 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 887 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 888 | .image = src_image, | ||
| 889 | .subresourceRange = src_range.SubresourceRange(src_aspect_mask), | ||
| 890 | }, | ||
| 891 | }; | ||
| 892 | const std::array middle_out_barrier{ | ||
| 893 | VkImageMemoryBarrier{ | ||
| 894 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 895 | .pNext = nullptr, | ||
| 896 | .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | | ||
| 897 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | | ||
| 898 | VK_ACCESS_TRANSFER_WRITE_BIT, | ||
| 899 | .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | ||
| 900 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 901 | .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | ||
| 902 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 903 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 904 | .image = dst_image, | ||
| 905 | .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask), | ||
| 906 | }, | ||
| 907 | }; | ||
| 908 | const std::array post_barriers{ | ||
| 909 | VkImageMemoryBarrier{ | ||
| 910 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 911 | .pNext = nullptr, | ||
| 912 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | ||
| 913 | .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | | ||
| 914 | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | | ||
| 915 | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | | ||
| 916 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | | ||
| 917 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | | ||
| 918 | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, | ||
| 919 | .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | ||
| 920 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 921 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 922 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 923 | .image = dst_image, | ||
| 924 | .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask), | ||
| 925 | }, | ||
| 926 | }; | ||
| 927 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, | ||
| 928 | 0, {}, {}, pre_barriers); | ||
| 929 | |||
| 930 | cmdbuf.CopyImageToBuffer(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, copy_buffer, | ||
| 931 | vk_in_copies); | ||
| 932 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 933 | 0, WRITE_BARRIER, nullptr, middle_in_barrier); | ||
| 934 | |||
| 935 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, | ||
| 936 | 0, READ_BARRIER, {}, middle_out_barrier); | ||
| 937 | cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies); | ||
| 938 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 939 | 0, {}, {}, post_barriers); | ||
| 940 | }); | ||
| 941 | } | ||
| 942 | |||
| 757 | void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, | 943 | void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, |
| 758 | const Region2D& dst_region, const Region2D& src_region, | 944 | const Region2D& dst_region, const Region2D& src_region, |
| 759 | Tegra::Engines::Fermi2D::Filter filter, | 945 | Tegra::Engines::Fermi2D::Filter filter, |
| @@ -881,6 +1067,11 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im | |||
| 881 | return blit_image_helper.ConvertD16ToR16(dst, src_view, up_scale, down_shift); | 1067 | return blit_image_helper.ConvertD16ToR16(dst, src_view, up_scale, down_shift); |
| 882 | } | 1068 | } |
| 883 | break; | 1069 | break; |
| 1070 | case PixelFormat::A8B8G8R8_UNORM: | ||
| 1071 | if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { | ||
| 1072 | return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view, up_scale, down_shift); | ||
| 1073 | } | ||
| 1074 | break; | ||
| 884 | case PixelFormat::R32_FLOAT: | 1075 | case PixelFormat::R32_FLOAT: |
| 885 | if (src_view.format == PixelFormat::D32_FLOAT) { | 1076 | if (src_view.format == PixelFormat::D32_FLOAT) { |
| 886 | return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift); | 1077 | return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift); |
| @@ -891,6 +1082,9 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im | |||
| 891 | return blit_image_helper.ConvertR16ToD16(dst, src_view, up_scale, down_shift); | 1082 | return blit_image_helper.ConvertR16ToD16(dst, src_view, up_scale, down_shift); |
| 892 | } | 1083 | } |
| 893 | break; | 1084 | break; |
| 1085 | case PixelFormat::S8_UINT_D24_UNORM: | ||
| 1086 | return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view, up_scale, down_shift); | ||
| 1087 | break; | ||
| 894 | case PixelFormat::D32_FLOAT: | 1088 | case PixelFormat::D32_FLOAT: |
| 895 | if (src_view.format == PixelFormat::R32_FLOAT) { | 1089 | if (src_view.format == PixelFormat::R32_FLOAT) { |
| 896 | return blit_image_helper.ConvertR32ToD32(dst, src_view, up_scale, down_shift); | 1090 | return blit_image_helper.ConvertR32ToD32(dst, src_view, up_scale, down_shift); |
| @@ -1386,6 +1580,14 @@ VkImageView ImageView::StencilView() { | |||
| 1386 | return *stencil_view; | 1580 | return *stencil_view; |
| 1387 | } | 1581 | } |
| 1388 | 1582 | ||
| 1583 | VkImageView ImageView::ColorView() { | ||
| 1584 | if (color_view) { | ||
| 1585 | return *color_view; | ||
| 1586 | } | ||
| 1587 | color_view = MakeView(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); | ||
| 1588 | return *color_view; | ||
| 1589 | } | ||
| 1590 | |||
| 1389 | VkImageView ImageView::StorageView(Shader::TextureType texture_type, | 1591 | VkImageView ImageView::StorageView(Shader::TextureType texture_type, |
| 1390 | Shader::ImageFormat image_format) { | 1592 | Shader::ImageFormat image_format) { |
| 1391 | if (image_format == Shader::ImageFormat::Typeless) { | 1593 | if (image_format == Shader::ImageFormat::Typeless) { |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index f5f8f9a74..753e3e8a1 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -61,6 +61,10 @@ public: | |||
| 61 | 61 | ||
| 62 | void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); | 62 | void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); |
| 63 | 63 | ||
| 64 | bool ShouldReinterpret(Image& dst, Image& src); | ||
| 65 | |||
| 66 | void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); | ||
| 67 | |||
| 64 | void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled); | 68 | void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled); |
| 65 | 69 | ||
| 66 | bool CanAccelerateImageUpload(Image&) const noexcept { | 70 | bool CanAccelerateImageUpload(Image&) const noexcept { |
| @@ -82,6 +86,8 @@ public: | |||
| 82 | return true; | 86 | return true; |
| 83 | } | 87 | } |
| 84 | 88 | ||
| 89 | [[nodiscard]] VkBuffer GetTemporaryBuffer(size_t needed_size); | ||
| 90 | |||
| 85 | const Device& device; | 91 | const Device& device; |
| 86 | VKScheduler& scheduler; | 92 | VKScheduler& scheduler; |
| 87 | MemoryAllocator& memory_allocator; | 93 | MemoryAllocator& memory_allocator; |
| @@ -90,6 +96,10 @@ public: | |||
| 90 | ASTCDecoderPass& astc_decoder_pass; | 96 | ASTCDecoderPass& astc_decoder_pass; |
| 91 | RenderPassCache& render_pass_cache; | 97 | RenderPassCache& render_pass_cache; |
| 92 | const Settings::ResolutionScalingInfo& resolution; | 98 | const Settings::ResolutionScalingInfo& resolution; |
| 99 | |||
| 100 | constexpr static size_t indexing_slots = 8 * sizeof(size_t); | ||
| 101 | std::array<vk::Buffer, indexing_slots> buffers{}; | ||
| 102 | std::array<std::unique_ptr<MemoryCommit>, indexing_slots> buffer_commits{}; | ||
| 93 | }; | 103 | }; |
| 94 | 104 | ||
| 95 | class Image : public VideoCommon::ImageBase { | 105 | class Image : public VideoCommon::ImageBase { |
| @@ -174,6 +184,8 @@ public: | |||
| 174 | 184 | ||
| 175 | [[nodiscard]] VkImageView StencilView(); | 185 | [[nodiscard]] VkImageView StencilView(); |
| 176 | 186 | ||
| 187 | [[nodiscard]] VkImageView ColorView(); | ||
| 188 | |||
| 177 | [[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type, | 189 | [[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type, |
| 178 | Shader::ImageFormat image_format); | 190 | Shader::ImageFormat image_format); |
| 179 | 191 | ||
| @@ -214,6 +226,7 @@ private: | |||
| 214 | std::unique_ptr<StorageViews> storage_views; | 226 | std::unique_ptr<StorageViews> storage_views; |
| 215 | vk::ImageView depth_view; | 227 | vk::ImageView depth_view; |
| 216 | vk::ImageView stencil_view; | 228 | vk::ImageView stencil_view; |
| 229 | vk::ImageView color_view; | ||
| 217 | VkImage image_handle = VK_NULL_HANDLE; | 230 | VkImage image_handle = VK_NULL_HANDLE; |
| 218 | VkImageView render_target = VK_NULL_HANDLE; | 231 | VkImageView render_target = VK_NULL_HANDLE; |
| 219 | VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; | 232 | VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; |
| @@ -316,7 +329,6 @@ struct TextureCacheParams { | |||
| 316 | static constexpr bool FRAMEBUFFER_BLITS = false; | 329 | static constexpr bool FRAMEBUFFER_BLITS = false; |
| 317 | static constexpr bool HAS_EMULATED_COPIES = false; | 330 | static constexpr bool HAS_EMULATED_COPIES = false; |
| 318 | static constexpr bool HAS_DEVICE_MEMORY_INFO = true; | 331 | static constexpr bool HAS_DEVICE_MEMORY_INFO = true; |
| 319 | static constexpr bool HAS_PIXEL_FORMAT_CONVERSIONS = false; | ||
| 320 | 332 | ||
| 321 | using Runtime = Vulkan::TextureCacheRuntime; | 333 | using Runtime = Vulkan::TextureCacheRuntime; |
| 322 | using Image = Vulkan::Image; | 334 | using Image = Vulkan::Image; |
diff --git a/src/video_core/shader_notify.cpp b/src/video_core/shader_notify.cpp index dc6995b46..bcaf5f575 100644 --- a/src/video_core/shader_notify.cpp +++ b/src/video_core/shader_notify.cpp | |||
| @@ -18,7 +18,7 @@ int ShaderNotify::ShadersBuilding() noexcept { | |||
| 18 | const int now_complete = num_complete.load(std::memory_order::relaxed); | 18 | const int now_complete = num_complete.load(std::memory_order::relaxed); |
| 19 | const int now_building = num_building.load(std::memory_order::relaxed); | 19 | const int now_building = num_building.load(std::memory_order::relaxed); |
| 20 | if (now_complete == now_building) { | 20 | if (now_complete == now_building) { |
| 21 | const auto now = std::chrono::high_resolution_clock::now(); | 21 | const auto now = std::chrono::steady_clock::now(); |
| 22 | if (completed && num_complete == num_when_completed) { | 22 | if (completed && num_complete == num_when_completed) { |
| 23 | if (now - complete_time > TIME_TO_STOP_REPORTING) { | 23 | if (now - complete_time > TIME_TO_STOP_REPORTING) { |
| 24 | report_base = now_complete; | 24 | report_base = now_complete; |
diff --git a/src/video_core/shader_notify.h b/src/video_core/shader_notify.h index ad363bfb5..4d8d52071 100644 --- a/src/video_core/shader_notify.h +++ b/src/video_core/shader_notify.h | |||
| @@ -28,6 +28,6 @@ private: | |||
| 28 | 28 | ||
| 29 | bool completed{}; | 29 | bool completed{}; |
| 30 | int num_when_completed{}; | 30 | int num_when_completed{}; |
| 31 | std::chrono::high_resolution_clock::time_point complete_time; | 31 | std::chrono::steady_clock::time_point complete_time; |
| 32 | }; | 32 | }; |
| 33 | } // namespace VideoCore | 33 | } // namespace VideoCore |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 58d262446..a36015c8c 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -82,6 +82,8 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { | |||
| 82 | return PixelFormat::D32_FLOAT; | 82 | return PixelFormat::D32_FLOAT; |
| 83 | case Tegra::DepthFormat::D16_UNORM: | 83 | case Tegra::DepthFormat::D16_UNORM: |
| 84 | return PixelFormat::D16_UNORM; | 84 | return PixelFormat::D16_UNORM; |
| 85 | case Tegra::DepthFormat::S8_UINT: | ||
| 86 | return PixelFormat::S8_UINT; | ||
| 85 | case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT: | 87 | case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT: |
| 86 | return PixelFormat::D32_FLOAT_S8_UINT; | 88 | return PixelFormat::D32_FLOAT_S8_UINT; |
| 87 | default: | 89 | default: |
| @@ -214,6 +216,11 @@ SurfaceType GetFormatType(PixelFormat pixel_format) { | |||
| 214 | } | 216 | } |
| 215 | 217 | ||
| 216 | if (static_cast<std::size_t>(pixel_format) < | 218 | if (static_cast<std::size_t>(pixel_format) < |
| 219 | static_cast<std::size_t>(PixelFormat::MaxStencilFormat)) { | ||
| 220 | return SurfaceType::Stencil; | ||
| 221 | } | ||
| 222 | |||
| 223 | if (static_cast<std::size_t>(pixel_format) < | ||
| 217 | static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) { | 224 | static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) { |
| 218 | return SurfaceType::DepthStencil; | 225 | return SurfaceType::DepthStencil; |
| 219 | } | 226 | } |
diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 2ce7c7d33..33e8d24ab 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h | |||
| @@ -110,8 +110,12 @@ enum class PixelFormat { | |||
| 110 | 110 | ||
| 111 | MaxDepthFormat, | 111 | MaxDepthFormat, |
| 112 | 112 | ||
| 113 | // Stencil formats | ||
| 114 | S8_UINT = MaxDepthFormat, | ||
| 115 | MaxStencilFormat, | ||
| 116 | |||
| 113 | // DepthStencil formats | 117 | // DepthStencil formats |
| 114 | D24_UNORM_S8_UINT = MaxDepthFormat, | 118 | D24_UNORM_S8_UINT = MaxStencilFormat, |
| 115 | S8_UINT_D24_UNORM, | 119 | S8_UINT_D24_UNORM, |
| 116 | D32_FLOAT_S8_UINT, | 120 | D32_FLOAT_S8_UINT, |
| 117 | 121 | ||
| @@ -125,8 +129,9 @@ constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max | |||
| 125 | enum class SurfaceType { | 129 | enum class SurfaceType { |
| 126 | ColorTexture = 0, | 130 | ColorTexture = 0, |
| 127 | Depth = 1, | 131 | Depth = 1, |
| 128 | DepthStencil = 2, | 132 | Stencil = 2, |
| 129 | Invalid = 3, | 133 | DepthStencil = 3, |
| 134 | Invalid = 4, | ||
| 130 | }; | 135 | }; |
| 131 | 136 | ||
| 132 | enum class SurfaceTarget { | 137 | enum class SurfaceTarget { |
| @@ -229,6 +234,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ | |||
| 229 | 1, // E5B9G9R9_FLOAT | 234 | 1, // E5B9G9R9_FLOAT |
| 230 | 1, // D32_FLOAT | 235 | 1, // D32_FLOAT |
| 231 | 1, // D16_UNORM | 236 | 1, // D16_UNORM |
| 237 | 1, // S8_UINT | ||
| 232 | 1, // D24_UNORM_S8_UINT | 238 | 1, // D24_UNORM_S8_UINT |
| 233 | 1, // S8_UINT_D24_UNORM | 239 | 1, // S8_UINT_D24_UNORM |
| 234 | 1, // D32_FLOAT_S8_UINT | 240 | 1, // D32_FLOAT_S8_UINT |
| @@ -328,6 +334,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ | |||
| 328 | 1, // E5B9G9R9_FLOAT | 334 | 1, // E5B9G9R9_FLOAT |
| 329 | 1, // D32_FLOAT | 335 | 1, // D32_FLOAT |
| 330 | 1, // D16_UNORM | 336 | 1, // D16_UNORM |
| 337 | 1, // S8_UINT | ||
| 331 | 1, // D24_UNORM_S8_UINT | 338 | 1, // D24_UNORM_S8_UINT |
| 332 | 1, // S8_UINT_D24_UNORM | 339 | 1, // S8_UINT_D24_UNORM |
| 333 | 1, // D32_FLOAT_S8_UINT | 340 | 1, // D32_FLOAT_S8_UINT |
| @@ -427,6 +434,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ | |||
| 427 | 32, // E5B9G9R9_FLOAT | 434 | 32, // E5B9G9R9_FLOAT |
| 428 | 32, // D32_FLOAT | 435 | 32, // D32_FLOAT |
| 429 | 16, // D16_UNORM | 436 | 16, // D16_UNORM |
| 437 | 8, // S8_UINT | ||
| 430 | 32, // D24_UNORM_S8_UINT | 438 | 32, // D24_UNORM_S8_UINT |
| 431 | 32, // S8_UINT_D24_UNORM | 439 | 32, // S8_UINT_D24_UNORM |
| 432 | 64, // D32_FLOAT_S8_UINT | 440 | 64, // D32_FLOAT_S8_UINT |
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index ddfb726fe..afa807d5d 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp | |||
| @@ -139,6 +139,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, | |||
| 139 | return PixelFormat::D16_UNORM; | 139 | return PixelFormat::D16_UNORM; |
| 140 | case Hash(TextureFormat::S8D24, UINT, UNORM, UNORM, UNORM, LINEAR): | 140 | case Hash(TextureFormat::S8D24, UINT, UNORM, UNORM, UNORM, LINEAR): |
| 141 | return PixelFormat::S8_UINT_D24_UNORM; | 141 | return PixelFormat::S8_UINT_D24_UNORM; |
| 142 | case Hash(TextureFormat::S8D24, UINT, UNORM, UINT, UINT, LINEAR): | ||
| 143 | return PixelFormat::S8_UINT_D24_UNORM; | ||
| 142 | case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR): | 144 | case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR): |
| 143 | return PixelFormat::S8_UINT_D24_UNORM; | 145 | return PixelFormat::S8_UINT_D24_UNORM; |
| 144 | case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): | 146 | case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): |
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index c6cf0583f..b2c81057b 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h | |||
| @@ -194,6 +194,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str | |||
| 194 | return "D32_FLOAT"; | 194 | return "D32_FLOAT"; |
| 195 | case PixelFormat::D16_UNORM: | 195 | case PixelFormat::D16_UNORM: |
| 196 | return "D16_UNORM"; | 196 | return "D16_UNORM"; |
| 197 | case PixelFormat::S8_UINT: | ||
| 198 | return "S8_UINT"; | ||
| 197 | case PixelFormat::D24_UNORM_S8_UINT: | 199 | case PixelFormat::D24_UNORM_S8_UINT: |
| 198 | return "D24_UNORM_S8_UINT"; | 200 | return "D24_UNORM_S8_UINT"; |
| 199 | case PixelFormat::S8_UINT_D24_UNORM: | 201 | case PixelFormat::S8_UINT_D24_UNORM: |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 241f71a91..5aaeb16ca 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -472,9 +472,10 @@ template <class P> | |||
| 472 | void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, | 472 | void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, |
| 473 | const Tegra::Engines::Fermi2D::Surface& src, | 473 | const Tegra::Engines::Fermi2D::Surface& src, |
| 474 | const Tegra::Engines::Fermi2D::Config& copy) { | 474 | const Tegra::Engines::Fermi2D::Config& copy) { |
| 475 | const BlitImages images = GetBlitImages(dst, src); | 475 | const BlitImages images = GetBlitImages(dst, src, copy); |
| 476 | const ImageId dst_id = images.dst_id; | 476 | const ImageId dst_id = images.dst_id; |
| 477 | const ImageId src_id = images.src_id; | 477 | const ImageId src_id = images.src_id; |
| 478 | |||
| 478 | PrepareImage(src_id, false, false); | 479 | PrepareImage(src_id, false, false); |
| 479 | PrepareImage(dst_id, true, false); | 480 | PrepareImage(dst_id, true, false); |
| 480 | 481 | ||
| @@ -758,14 +759,18 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, | |||
| 758 | return ImageId{}; | 759 | return ImageId{}; |
| 759 | } | 760 | } |
| 760 | } | 761 | } |
| 761 | const bool broken_views = runtime.HasBrokenTextureViewFormats(); | 762 | const bool broken_views = |
| 763 | runtime.HasBrokenTextureViewFormats() || True(options & RelaxedOptions::ForceBrokenViews); | ||
| 762 | const bool native_bgr = runtime.HasNativeBgr(); | 764 | const bool native_bgr = runtime.HasNativeBgr(); |
| 763 | ImageId image_id; | 765 | const bool flexible_formats = True(options & RelaxedOptions::Format); |
| 766 | ImageId image_id{}; | ||
| 767 | boost::container::small_vector<ImageId, 1> image_ids; | ||
| 764 | const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { | 768 | const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { |
| 765 | if (True(existing_image.flags & ImageFlagBits::Remapped)) { | 769 | if (True(existing_image.flags & ImageFlagBits::Remapped)) { |
| 766 | return false; | 770 | return false; |
| 767 | } | 771 | } |
| 768 | if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { | 772 | if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) |
| 773 | [[unlikely]] { | ||
| 769 | const bool strict_size = False(options & RelaxedOptions::Size) && | 774 | const bool strict_size = False(options & RelaxedOptions::Size) && |
| 770 | True(existing_image.flags & ImageFlagBits::Strong); | 775 | True(existing_image.flags & ImageFlagBits::Strong); |
| 771 | const ImageInfo& existing = existing_image.info; | 776 | const ImageInfo& existing = existing_image.info; |
| @@ -774,17 +779,27 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, | |||
| 774 | IsPitchLinearSameSize(existing, info, strict_size) && | 779 | IsPitchLinearSameSize(existing, info, strict_size) && |
| 775 | IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) { | 780 | IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) { |
| 776 | image_id = existing_image_id; | 781 | image_id = existing_image_id; |
| 777 | return true; | 782 | image_ids.push_back(existing_image_id); |
| 783 | return !flexible_formats && existing.format == info.format; | ||
| 778 | } | 784 | } |
| 779 | } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views, | 785 | } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views, |
| 780 | native_bgr)) { | 786 | native_bgr)) { |
| 781 | image_id = existing_image_id; | 787 | image_id = existing_image_id; |
| 782 | return true; | 788 | image_ids.push_back(existing_image_id); |
| 789 | return !flexible_formats && existing_image.info.format == info.format; | ||
| 783 | } | 790 | } |
| 784 | return false; | 791 | return false; |
| 785 | }; | 792 | }; |
| 786 | ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda); | 793 | ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda); |
| 787 | return image_id; | 794 | if (image_ids.size() <= 1) [[likely]] { |
| 795 | return image_id; | ||
| 796 | } | ||
| 797 | auto image_ids_compare = [this](ImageId a, ImageId b) { | ||
| 798 | auto& image_a = slot_images[a]; | ||
| 799 | auto& image_b = slot_images[b]; | ||
| 800 | return image_a.modification_tick < image_b.modification_tick; | ||
| 801 | }; | ||
| 802 | return *std::ranges::max_element(image_ids, image_ids_compare); | ||
| 788 | } | 803 | } |
| 789 | 804 | ||
| 790 | template <class P> | 805 | template <class P> |
| @@ -1076,31 +1091,66 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA | |||
| 1076 | 1091 | ||
| 1077 | template <class P> | 1092 | template <class P> |
| 1078 | typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages( | 1093 | typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages( |
| 1079 | const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src) { | 1094 | const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, |
| 1080 | static constexpr auto FIND_OPTIONS = RelaxedOptions::Format | RelaxedOptions::Samples; | 1095 | const Tegra::Engines::Fermi2D::Config& copy) { |
| 1096 | |||
| 1097 | static constexpr auto FIND_OPTIONS = RelaxedOptions::Samples; | ||
| 1081 | const GPUVAddr dst_addr = dst.Address(); | 1098 | const GPUVAddr dst_addr = dst.Address(); |
| 1082 | const GPUVAddr src_addr = src.Address(); | 1099 | const GPUVAddr src_addr = src.Address(); |
| 1083 | ImageInfo dst_info(dst); | 1100 | ImageInfo dst_info(dst); |
| 1084 | ImageInfo src_info(src); | 1101 | ImageInfo src_info(src); |
| 1102 | const bool can_be_depth_blit = | ||
| 1103 | dst_info.format == src_info.format && copy.filter == Tegra::Engines::Fermi2D::Filter::Point; | ||
| 1085 | ImageId dst_id; | 1104 | ImageId dst_id; |
| 1086 | ImageId src_id; | 1105 | ImageId src_id; |
| 1106 | RelaxedOptions try_options = FIND_OPTIONS; | ||
| 1107 | if (can_be_depth_blit) { | ||
| 1108 | try_options |= RelaxedOptions::Format; | ||
| 1109 | } | ||
| 1087 | do { | 1110 | do { |
| 1088 | has_deleted_images = false; | 1111 | has_deleted_images = false; |
| 1089 | dst_id = FindImage(dst_info, dst_addr, FIND_OPTIONS); | 1112 | src_id = FindImage(src_info, src_addr, try_options); |
| 1090 | src_id = FindImage(src_info, src_addr, FIND_OPTIONS); | 1113 | dst_id = FindImage(dst_info, dst_addr, try_options); |
| 1091 | const ImageBase* const dst_image = dst_id ? &slot_images[dst_id] : nullptr; | ||
| 1092 | const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr; | 1114 | const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr; |
| 1093 | DeduceBlitImages(dst_info, src_info, dst_image, src_image); | 1115 | if (src_image && src_image->info.num_samples > 1) { |
| 1094 | if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) { | 1116 | RelaxedOptions find_options{FIND_OPTIONS | RelaxedOptions::ForceBrokenViews}; |
| 1095 | continue; | 1117 | src_id = FindOrInsertImage(src_info, src_addr, find_options); |
| 1118 | dst_id = FindOrInsertImage(dst_info, dst_addr, find_options); | ||
| 1119 | if (has_deleted_images) { | ||
| 1120 | continue; | ||
| 1121 | } | ||
| 1122 | break; | ||
| 1096 | } | 1123 | } |
| 1097 | if (!dst_id) { | 1124 | if (can_be_depth_blit) { |
| 1098 | dst_id = InsertImage(dst_info, dst_addr, RelaxedOptions{}); | 1125 | const ImageBase* const dst_image = dst_id ? &slot_images[dst_id] : nullptr; |
| 1126 | DeduceBlitImages(dst_info, src_info, dst_image, src_image); | ||
| 1127 | if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) { | ||
| 1128 | continue; | ||
| 1129 | } | ||
| 1099 | } | 1130 | } |
| 1100 | if (!src_id) { | 1131 | if (!src_id) { |
| 1101 | src_id = InsertImage(src_info, src_addr, RelaxedOptions{}); | 1132 | src_id = InsertImage(src_info, src_addr, RelaxedOptions{}); |
| 1102 | } | 1133 | } |
| 1134 | if (!dst_id) { | ||
| 1135 | dst_id = InsertImage(dst_info, dst_addr, RelaxedOptions{}); | ||
| 1136 | } | ||
| 1103 | } while (has_deleted_images); | 1137 | } while (has_deleted_images); |
| 1138 | const ImageBase& src_image = slot_images[src_id]; | ||
| 1139 | const ImageBase& dst_image = slot_images[dst_id]; | ||
| 1140 | const bool native_bgr = runtime.HasNativeBgr(); | ||
| 1141 | if (GetFormatType(dst_info.format) != GetFormatType(dst_image.info.format) || | ||
| 1142 | GetFormatType(src_info.format) != GetFormatType(src_image.info.format) || | ||
| 1143 | !VideoCore::Surface::IsViewCompatible(dst_info.format, dst_image.info.format, false, | ||
| 1144 | native_bgr) || | ||
| 1145 | !VideoCore::Surface::IsViewCompatible(src_info.format, src_image.info.format, false, | ||
| 1146 | native_bgr)) { | ||
| 1147 | // Make sure the images match the expected format. | ||
| 1148 | do { | ||
| 1149 | has_deleted_images = false; | ||
| 1150 | src_id = FindOrInsertImage(src_info, src_addr, RelaxedOptions{}); | ||
| 1151 | dst_id = FindOrInsertImage(dst_info, dst_addr, RelaxedOptions{}); | ||
| 1152 | } while (has_deleted_images); | ||
| 1153 | } | ||
| 1104 | return BlitImages{ | 1154 | return BlitImages{ |
| 1105 | .dst_id = dst_id, | 1155 | .dst_id = dst_id, |
| 1106 | .src_id = src_id, | 1156 | .src_id = src_id, |
| @@ -1157,7 +1207,14 @@ template <class P> | |||
| 1157 | ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, | 1207 | ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, |
| 1158 | bool is_clear) { | 1208 | bool is_clear) { |
| 1159 | const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{}; | 1209 | const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{}; |
| 1160 | const ImageId image_id = FindOrInsertImage(info, gpu_addr, options); | 1210 | ImageId image_id{}; |
| 1211 | bool delete_state = has_deleted_images; | ||
| 1212 | do { | ||
| 1213 | has_deleted_images = false; | ||
| 1214 | image_id = FindOrInsertImage(info, gpu_addr, options); | ||
| 1215 | delete_state |= has_deleted_images; | ||
| 1216 | } while (has_deleted_images); | ||
| 1217 | has_deleted_images = delete_state; | ||
| 1161 | if (!image_id) { | 1218 | if (!image_id) { |
| 1162 | return NULL_IMAGE_VIEW_ID; | 1219 | return NULL_IMAGE_VIEW_ID; |
| 1163 | } | 1220 | } |
| @@ -1759,8 +1816,8 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag | |||
| 1759 | } | 1816 | } |
| 1760 | UNIMPLEMENTED_IF(dst.info.type != ImageType::e2D); | 1817 | UNIMPLEMENTED_IF(dst.info.type != ImageType::e2D); |
| 1761 | UNIMPLEMENTED_IF(src.info.type != ImageType::e2D); | 1818 | UNIMPLEMENTED_IF(src.info.type != ImageType::e2D); |
| 1762 | if constexpr (HAS_PIXEL_FORMAT_CONVERSIONS) { | 1819 | if (runtime.ShouldReinterpret(dst, src)) { |
| 1763 | return runtime.ConvertImage(dst, src, copies); | 1820 | return runtime.ReinterpretImage(dst, src, copies); |
| 1764 | } | 1821 | } |
| 1765 | for (const ImageCopy& copy : copies) { | 1822 | for (const ImageCopy& copy : copies) { |
| 1766 | UNIMPLEMENTED_IF(copy.dst_subresource.num_layers != 1); | 1823 | UNIMPLEMENTED_IF(copy.dst_subresource.num_layers != 1); |
| @@ -1780,7 +1837,13 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag | |||
| 1780 | const SubresourceExtent src_extent{.levels = 1, .layers = 1}; | 1837 | const SubresourceExtent src_extent{.levels = 1, .layers = 1}; |
| 1781 | const SubresourceRange dst_range{.base = dst_base, .extent = dst_extent}; | 1838 | const SubresourceRange dst_range{.base = dst_base, .extent = dst_extent}; |
| 1782 | const SubresourceRange src_range{.base = src_base, .extent = src_extent}; | 1839 | const SubresourceRange src_range{.base = src_base, .extent = src_extent}; |
| 1783 | const ImageViewInfo dst_view_info(ImageViewType::e2D, dst.info.format, dst_range); | 1840 | PixelFormat dst_format = dst.info.format; |
| 1841 | if (GetFormatType(src.info.format) == SurfaceType::DepthStencil && | ||
| 1842 | GetFormatType(dst_format) == SurfaceType::ColorTexture && | ||
| 1843 | BytesPerBlock(dst_format) == 4) { | ||
| 1844 | dst_format = PixelFormat::A8B8G8R8_UNORM; | ||
| 1845 | } | ||
| 1846 | const ImageViewInfo dst_view_info(ImageViewType::e2D, dst_format, dst_range); | ||
| 1784 | const ImageViewInfo src_view_info(ImageViewType::e2D, src.info.format, src_range); | 1847 | const ImageViewInfo src_view_info(ImageViewType::e2D, src.info.format, src_range); |
| 1785 | const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info); | 1848 | const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info); |
| 1786 | Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id]; | 1849 | Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id]; |
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index a9504c0e8..7107887a6 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h | |||
| @@ -59,8 +59,6 @@ class TextureCache { | |||
| 59 | static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; | 59 | static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; |
| 60 | /// True when the API can provide info about the memory of the device. | 60 | /// True when the API can provide info about the memory of the device. |
| 61 | static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; | 61 | static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; |
| 62 | /// True when the API provides utilities for pixel format conversions. | ||
| 63 | static constexpr bool HAS_PIXEL_FORMAT_CONVERSIONS = P::HAS_PIXEL_FORMAT_CONVERSIONS; | ||
| 64 | 62 | ||
| 65 | static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; | 63 | static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; |
| 66 | static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; | 64 | static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; |
| @@ -254,7 +252,8 @@ private: | |||
| 254 | 252 | ||
| 255 | /// Return a blit image pair from the given guest blit parameters | 253 | /// Return a blit image pair from the given guest blit parameters |
| 256 | [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst, | 254 | [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst, |
| 257 | const Tegra::Engines::Fermi2D::Surface& src); | 255 | const Tegra::Engines::Fermi2D::Surface& src, |
| 256 | const Tegra::Engines::Fermi2D::Config& copy); | ||
| 258 | 257 | ||
| 259 | /// Find or create a sampler from a guest descriptor sampler | 258 | /// Find or create a sampler from a guest descriptor sampler |
| 260 | [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); | 259 | [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); |
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h index 5c274abdf..5ac27b3a7 100644 --- a/src/video_core/texture_cache/types.h +++ b/src/video_core/texture_cache/types.h | |||
| @@ -54,6 +54,7 @@ enum class RelaxedOptions : u32 { | |||
| 54 | Size = 1 << 0, | 54 | Size = 1 << 0, |
| 55 | Format = 1 << 1, | 55 | Format = 1 << 1, |
| 56 | Samples = 1 << 2, | 56 | Samples = 1 << 2, |
| 57 | ForceBrokenViews = 1 << 3, | ||
| 57 | }; | 58 | }; |
| 58 | DECLARE_ENUM_FLAG_OPERATORS(RelaxedOptions) | 59 | DECLARE_ENUM_FLAG_OPERATORS(RelaxedOptions) |
| 59 | 60 | ||
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index ddc9fb13a..7bd31b211 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp | |||
| @@ -1151,6 +1151,7 @@ bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr | |||
| 1151 | 1151 | ||
| 1152 | void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, | 1152 | void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, |
| 1153 | const ImageBase* src) { | 1153 | const ImageBase* src) { |
| 1154 | const auto original_dst_format = dst_info.format; | ||
| 1154 | if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) { | 1155 | if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) { |
| 1155 | src_info.format = src->info.format; | 1156 | src_info.format = src->info.format; |
| 1156 | } | 1157 | } |
| @@ -1161,7 +1162,13 @@ void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* | |||
| 1161 | dst_info.format = src->info.format; | 1162 | dst_info.format = src->info.format; |
| 1162 | } | 1163 | } |
| 1163 | if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) { | 1164 | if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) { |
| 1164 | src_info.format = dst->info.format; | 1165 | if (src) { |
| 1166 | if (GetFormatType(src->info.format) == SurfaceType::ColorTexture) { | ||
| 1167 | dst_info.format = original_dst_format; | ||
| 1168 | } | ||
| 1169 | } else { | ||
| 1170 | src_info.format = dst->info.format; | ||
| 1171 | } | ||
| 1165 | } | 1172 | } |
| 1166 | } | 1173 | } |
| 1167 | 1174 | ||
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index e852c817e..329bf4def 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -55,10 +55,4 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor | |||
| 55 | } | 55 | } |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | float GetResolutionScaleFactor(const RendererBase& renderer) { | ||
| 59 | return Settings::values.resolution_info.active | ||
| 60 | ? Settings::values.resolution_info.up_factor | ||
| 61 | : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio(); | ||
| 62 | } | ||
| 63 | |||
| 64 | } // namespace VideoCore | 58 | } // namespace VideoCore |
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index f86877e86..084df641f 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h | |||
| @@ -25,6 +25,4 @@ class RendererBase; | |||
| 25 | /// Creates an emulated GPU instance using the given system context. | 25 | /// Creates an emulated GPU instance using the given system context. |
| 26 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system); | 26 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system); |
| 27 | 27 | ||
| 28 | float GetResolutionScaleFactor(const RendererBase& renderer); | ||
| 29 | |||
| 30 | } // namespace VideoCore | 28 | } // namespace VideoCore |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 95106f88f..7bf5b6578 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -21,6 +21,13 @@ | |||
| 21 | namespace Vulkan { | 21 | namespace Vulkan { |
| 22 | namespace { | 22 | namespace { |
| 23 | namespace Alternatives { | 23 | namespace Alternatives { |
| 24 | constexpr std::array STENCIL8_UINT{ | ||
| 25 | VK_FORMAT_D16_UNORM_S8_UINT, | ||
| 26 | VK_FORMAT_D24_UNORM_S8_UINT, | ||
| 27 | VK_FORMAT_D32_SFLOAT_S8_UINT, | ||
| 28 | VK_FORMAT_UNDEFINED, | ||
| 29 | }; | ||
| 30 | |||
| 24 | constexpr std::array DEPTH24_UNORM_STENCIL8_UINT{ | 31 | constexpr std::array DEPTH24_UNORM_STENCIL8_UINT{ |
| 25 | VK_FORMAT_D32_SFLOAT_S8_UINT, | 32 | VK_FORMAT_D32_SFLOAT_S8_UINT, |
| 26 | VK_FORMAT_D16_UNORM_S8_UINT, | 33 | VK_FORMAT_D16_UNORM_S8_UINT, |
| @@ -74,6 +81,8 @@ void SetNext(void**& next, T& data) { | |||
| 74 | 81 | ||
| 75 | constexpr const VkFormat* GetFormatAlternatives(VkFormat format) { | 82 | constexpr const VkFormat* GetFormatAlternatives(VkFormat format) { |
| 76 | switch (format) { | 83 | switch (format) { |
| 84 | case VK_FORMAT_S8_UINT: | ||
| 85 | return Alternatives::STENCIL8_UINT.data(); | ||
| 77 | case VK_FORMAT_D24_UNORM_S8_UINT: | 86 | case VK_FORMAT_D24_UNORM_S8_UINT: |
| 78 | return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data(); | 87 | return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data(); |
| 79 | case VK_FORMAT_D16_UNORM_S8_UINT: | 88 | case VK_FORMAT_D16_UNORM_S8_UINT: |
| @@ -121,6 +130,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica | |||
| 121 | VK_FORMAT_R16G16_UNORM, | 130 | VK_FORMAT_R16G16_UNORM, |
| 122 | VK_FORMAT_R16G16_SNORM, | 131 | VK_FORMAT_R16G16_SNORM, |
| 123 | VK_FORMAT_R16G16_SFLOAT, | 132 | VK_FORMAT_R16G16_SFLOAT, |
| 133 | VK_FORMAT_R16G16_UINT, | ||
| 124 | VK_FORMAT_R16G16_SINT, | 134 | VK_FORMAT_R16G16_SINT, |
| 125 | VK_FORMAT_R16_UNORM, | 135 | VK_FORMAT_R16_UNORM, |
| 126 | VK_FORMAT_R16_SNORM, | 136 | VK_FORMAT_R16_SNORM, |
| @@ -145,6 +155,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica | |||
| 145 | VK_FORMAT_R4G4B4A4_UNORM_PACK16, | 155 | VK_FORMAT_R4G4B4A4_UNORM_PACK16, |
| 146 | VK_FORMAT_D32_SFLOAT, | 156 | VK_FORMAT_D32_SFLOAT, |
| 147 | VK_FORMAT_D16_UNORM, | 157 | VK_FORMAT_D16_UNORM, |
| 158 | VK_FORMAT_S8_UINT, | ||
| 148 | VK_FORMAT_D16_UNORM_S8_UINT, | 159 | VK_FORMAT_D16_UNORM_S8_UINT, |
| 149 | VK_FORMAT_D24_UNORM_S8_UINT, | 160 | VK_FORMAT_D24_UNORM_S8_UINT, |
| 150 | VK_FORMAT_D32_SFLOAT_S8_UINT, | 161 | VK_FORMAT_D32_SFLOAT_S8_UINT, |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index d62fd566f..732e8c276 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -90,9 +90,6 @@ add_executable(yuzu | |||
| 90 | configuration/configure_motion_touch.cpp | 90 | configuration/configure_motion_touch.cpp |
| 91 | configuration/configure_motion_touch.h | 91 | configuration/configure_motion_touch.h |
| 92 | configuration/configure_motion_touch.ui | 92 | configuration/configure_motion_touch.ui |
| 93 | configuration/configure_mouse_advanced.cpp | ||
| 94 | configuration/configure_mouse_advanced.h | ||
| 95 | configuration/configure_mouse_advanced.ui | ||
| 96 | configuration/configure_per_game.cpp | 93 | configuration/configure_per_game.cpp |
| 97 | configuration/configure_per_game.h | 94 | configuration/configure_per_game.h |
| 98 | configuration/configure_per_game.ui | 95 | configuration/configure_per_game.ui |
| @@ -155,6 +152,8 @@ add_executable(yuzu | |||
| 155 | main.ui | 152 | main.ui |
| 156 | uisettings.cpp | 153 | uisettings.cpp |
| 157 | uisettings.h | 154 | uisettings.h |
| 155 | util/controller_navigation.cpp | ||
| 156 | util/controller_navigation.h | ||
| 158 | util/limitable_input_dialog.cpp | 157 | util/limitable_input_dialog.cpp |
| 159 | util/limitable_input_dialog.h | 158 | util/limitable_input_dialog.h |
| 160 | util/overlay_dialog.cpp | 159 | util/overlay_dialog.cpp |
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index bf8445a89..c5685db2e 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp | |||
| @@ -6,8 +6,12 @@ | |||
| 6 | #include <thread> | 6 | #include <thread> |
| 7 | 7 | ||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/param_package.h" | ||
| 9 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 10 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/hid/emulated_controller.h" | ||
| 13 | #include "core/hid/hid_core.h" | ||
| 14 | #include "core/hid/hid_types.h" | ||
| 11 | #include "core/hle/lock.h" | 15 | #include "core/hle/lock.h" |
| 12 | #include "core/hle/service/hid/controllers/npad.h" | 16 | #include "core/hle/service/hid/controllers/npad.h" |
| 13 | #include "core/hle/service/hid/hid.h" | 17 | #include "core/hle/service/hid/hid.h" |
| @@ -23,49 +27,32 @@ | |||
| 23 | 27 | ||
| 24 | namespace { | 28 | namespace { |
| 25 | 29 | ||
| 26 | constexpr std::size_t HANDHELD_INDEX = 8; | 30 | void UpdateController(Core::HID::EmulatedController* controller, |
| 27 | 31 | Core::HID::NpadStyleIndex controller_type, bool connected) { | |
| 28 | constexpr std::array<std::array<bool, 4>, 8> led_patterns{{ | 32 | if (controller->IsConnected(true)) { |
| 29 | {true, false, false, false}, | 33 | controller->Disconnect(); |
| 30 | {true, true, false, false}, | 34 | } |
| 31 | {true, true, true, false}, | 35 | controller->SetNpadStyleIndex(controller_type); |
| 32 | {true, true, true, true}, | 36 | if (connected) { |
| 33 | {true, false, false, true}, | 37 | controller->Connect(); |
| 34 | {true, false, true, false}, | ||
| 35 | {true, false, true, true}, | ||
| 36 | {false, true, true, false}, | ||
| 37 | }}; | ||
| 38 | |||
| 39 | void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, | ||
| 40 | bool connected, Core::System& system) { | ||
| 41 | if (!system.IsPoweredOn()) { | ||
| 42 | return; | ||
| 43 | } | 38 | } |
| 44 | |||
| 45 | auto& npad = | ||
| 46 | system.ServiceManager() | ||
| 47 | .GetService<Service::HID::Hid>("hid") | ||
| 48 | ->GetAppletResource() | ||
| 49 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); | ||
| 50 | |||
| 51 | npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected); | ||
| 52 | } | 39 | } |
| 53 | 40 | ||
| 54 | // Returns true if the given controller type is compatible with the given parameters. | 41 | // Returns true if the given controller type is compatible with the given parameters. |
| 55 | bool IsControllerCompatible(Settings::ControllerType controller_type, | 42 | bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type, |
| 56 | Core::Frontend::ControllerParameters parameters) { | 43 | Core::Frontend::ControllerParameters parameters) { |
| 57 | switch (controller_type) { | 44 | switch (controller_type) { |
| 58 | case Settings::ControllerType::ProController: | 45 | case Core::HID::NpadStyleIndex::ProController: |
| 59 | return parameters.allow_pro_controller; | 46 | return parameters.allow_pro_controller; |
| 60 | case Settings::ControllerType::DualJoyconDetached: | 47 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 61 | return parameters.allow_dual_joycons; | 48 | return parameters.allow_dual_joycons; |
| 62 | case Settings::ControllerType::LeftJoycon: | 49 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 63 | return parameters.allow_left_joycon; | 50 | return parameters.allow_left_joycon; |
| 64 | case Settings::ControllerType::RightJoycon: | 51 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 65 | return parameters.allow_right_joycon; | 52 | return parameters.allow_right_joycon; |
| 66 | case Settings::ControllerType::Handheld: | 53 | case Core::HID::NpadStyleIndex::Handheld: |
| 67 | return parameters.enable_single_mode && parameters.allow_handheld; | 54 | return parameters.enable_single_mode && parameters.allow_handheld; |
| 68 | case Settings::ControllerType::GameCube: | 55 | case Core::HID::NpadStyleIndex::GameCube: |
| 69 | return parameters.allow_gamecube_controller; | 56 | return parameters.allow_gamecube_controller; |
| 70 | default: | 57 | default: |
| 71 | return false; | 58 | return false; |
| @@ -196,7 +183,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 196 | connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), | 183 | connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), |
| 197 | [this, i](int index) { | 184 | [this, i](int index) { |
| 198 | UpdateDockedState(GetControllerTypeFromIndex(index, i) == | 185 | UpdateDockedState(GetControllerTypeFromIndex(index, i) == |
| 199 | Settings::ControllerType::Handheld); | 186 | Core::HID::NpadStyleIndex::Handheld); |
| 200 | }); | 187 | }); |
| 201 | } | 188 | } |
| 202 | } | 189 | } |
| @@ -230,7 +217,9 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 230 | resize(0, 0); | 217 | resize(0, 0); |
| 231 | } | 218 | } |
| 232 | 219 | ||
| 233 | QtControllerSelectorDialog::~QtControllerSelectorDialog() = default; | 220 | QtControllerSelectorDialog::~QtControllerSelectorDialog() { |
| 221 | system.HIDCore().DisableAllControllerConfiguration(); | ||
| 222 | } | ||
| 234 | 223 | ||
| 235 | int QtControllerSelectorDialog::exec() { | 224 | int QtControllerSelectorDialog::exec() { |
| 236 | if (parameters_met && parameters.enable_single_mode) { | 225 | if (parameters_met && parameters.enable_single_mode) { |
| @@ -249,17 +238,20 @@ void QtControllerSelectorDialog::ApplyConfiguration() { | |||
| 249 | } | 238 | } |
| 250 | 239 | ||
| 251 | void QtControllerSelectorDialog::LoadConfiguration() { | 240 | void QtControllerSelectorDialog::LoadConfiguration() { |
| 241 | system.HIDCore().EnableAllControllerConfiguration(); | ||
| 242 | |||
| 243 | const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 252 | for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { | 244 | for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { |
| 245 | const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index); | ||
| 253 | const auto connected = | 246 | const auto connected = |
| 254 | Settings::values.players.GetValue()[index].connected || | 247 | controller->IsConnected(true) || (index == 0 && handheld->IsConnected(true)); |
| 255 | (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected); | ||
| 256 | player_groupboxes[index]->setChecked(connected); | 248 | player_groupboxes[index]->setChecked(connected); |
| 257 | connected_controller_checkboxes[index]->setChecked(connected); | 249 | connected_controller_checkboxes[index]->setChecked(connected); |
| 258 | emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType( | 250 | emulated_controllers[index]->setCurrentIndex( |
| 259 | Settings::values.players.GetValue()[index].controller_type, index)); | 251 | GetIndexFromControllerType(controller->GetNpadStyleIndex(true), index)); |
| 260 | } | 252 | } |
| 261 | 253 | ||
| 262 | UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); | 254 | UpdateDockedState(handheld->IsConnected(true)); |
| 263 | 255 | ||
| 264 | ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); | 256 | ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); |
| 265 | ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); | 257 | ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); |
| @@ -415,33 +407,33 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index | |||
| 415 | emulated_controllers[player_index]->clear(); | 407 | emulated_controllers[player_index]->clear(); |
| 416 | 408 | ||
| 417 | pairs.emplace_back(emulated_controllers[player_index]->count(), | 409 | pairs.emplace_back(emulated_controllers[player_index]->count(), |
| 418 | Settings::ControllerType::ProController); | 410 | Core::HID::NpadStyleIndex::ProController); |
| 419 | emulated_controllers[player_index]->addItem(tr("Pro Controller")); | 411 | emulated_controllers[player_index]->addItem(tr("Pro Controller")); |
| 420 | 412 | ||
| 421 | pairs.emplace_back(emulated_controllers[player_index]->count(), | 413 | pairs.emplace_back(emulated_controllers[player_index]->count(), |
| 422 | Settings::ControllerType::DualJoyconDetached); | 414 | Core::HID::NpadStyleIndex::JoyconDual); |
| 423 | emulated_controllers[player_index]->addItem(tr("Dual Joycons")); | 415 | emulated_controllers[player_index]->addItem(tr("Dual Joycons")); |
| 424 | 416 | ||
| 425 | pairs.emplace_back(emulated_controllers[player_index]->count(), | 417 | pairs.emplace_back(emulated_controllers[player_index]->count(), |
| 426 | Settings::ControllerType::LeftJoycon); | 418 | Core::HID::NpadStyleIndex::JoyconLeft); |
| 427 | emulated_controllers[player_index]->addItem(tr("Left Joycon")); | 419 | emulated_controllers[player_index]->addItem(tr("Left Joycon")); |
| 428 | 420 | ||
| 429 | pairs.emplace_back(emulated_controllers[player_index]->count(), | 421 | pairs.emplace_back(emulated_controllers[player_index]->count(), |
| 430 | Settings::ControllerType::RightJoycon); | 422 | Core::HID::NpadStyleIndex::JoyconRight); |
| 431 | emulated_controllers[player_index]->addItem(tr("Right Joycon")); | 423 | emulated_controllers[player_index]->addItem(tr("Right Joycon")); |
| 432 | 424 | ||
| 433 | if (player_index == 0) { | 425 | if (player_index == 0) { |
| 434 | pairs.emplace_back(emulated_controllers[player_index]->count(), | 426 | pairs.emplace_back(emulated_controllers[player_index]->count(), |
| 435 | Settings::ControllerType::Handheld); | 427 | Core::HID::NpadStyleIndex::Handheld); |
| 436 | emulated_controllers[player_index]->addItem(tr("Handheld")); | 428 | emulated_controllers[player_index]->addItem(tr("Handheld")); |
| 437 | } | 429 | } |
| 438 | 430 | ||
| 439 | pairs.emplace_back(emulated_controllers[player_index]->count(), | 431 | pairs.emplace_back(emulated_controllers[player_index]->count(), |
| 440 | Settings::ControllerType::GameCube); | 432 | Core::HID::NpadStyleIndex::GameCube); |
| 441 | emulated_controllers[player_index]->addItem(tr("GameCube Controller")); | 433 | emulated_controllers[player_index]->addItem(tr("GameCube Controller")); |
| 442 | } | 434 | } |
| 443 | 435 | ||
| 444 | Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( | 436 | Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex( |
| 445 | int index, std::size_t player_index) const { | 437 | int index, std::size_t player_index) const { |
| 446 | const auto& pairs = index_controller_type_pairs[player_index]; | 438 | const auto& pairs = index_controller_type_pairs[player_index]; |
| 447 | 439 | ||
| @@ -449,13 +441,13 @@ Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( | |||
| 449 | [index](const auto& pair) { return pair.first == index; }); | 441 | [index](const auto& pair) { return pair.first == index; }); |
| 450 | 442 | ||
| 451 | if (it == pairs.end()) { | 443 | if (it == pairs.end()) { |
| 452 | return Settings::ControllerType::ProController; | 444 | return Core::HID::NpadStyleIndex::ProController; |
| 453 | } | 445 | } |
| 454 | 446 | ||
| 455 | return it->second; | 447 | return it->second; |
| 456 | } | 448 | } |
| 457 | 449 | ||
| 458 | int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type, | 450 | int QtControllerSelectorDialog::GetIndexFromControllerType(Core::HID::NpadStyleIndex type, |
| 459 | std::size_t player_index) const { | 451 | std::size_t player_index) const { |
| 460 | const auto& pairs = index_controller_type_pairs[player_index]; | 452 | const auto& pairs = index_controller_type_pairs[player_index]; |
| 461 | 453 | ||
| @@ -479,16 +471,16 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) | |||
| 479 | const QString stylesheet = [this, player_index] { | 471 | const QString stylesheet = [this, player_index] { |
| 480 | switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), | 472 | switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), |
| 481 | player_index)) { | 473 | player_index)) { |
| 482 | case Settings::ControllerType::ProController: | 474 | case Core::HID::NpadStyleIndex::ProController: |
| 483 | case Settings::ControllerType::GameCube: | 475 | case Core::HID::NpadStyleIndex::GameCube: |
| 484 | return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); | 476 | return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); |
| 485 | case Settings::ControllerType::DualJoyconDetached: | 477 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 486 | return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); "); | 478 | return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); "); |
| 487 | case Settings::ControllerType::LeftJoycon: | 479 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 488 | return QStringLiteral("image: url(:/controller/applet_joycon_left%0); "); | 480 | return QStringLiteral("image: url(:/controller/applet_joycon_left%0); "); |
| 489 | case Settings::ControllerType::RightJoycon: | 481 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 490 | return QStringLiteral("image: url(:/controller/applet_joycon_right%0); "); | 482 | return QStringLiteral("image: url(:/controller/applet_joycon_right%0); "); |
| 491 | case Settings::ControllerType::Handheld: | 483 | case Core::HID::NpadStyleIndex::Handheld: |
| 492 | return QStringLiteral("image: url(:/controller/applet_handheld%0); "); | 484 | return QStringLiteral("image: url(:/controller/applet_handheld%0); "); |
| 493 | default: | 485 | default: |
| 494 | return QString{}; | 486 | return QString{}; |
| @@ -516,54 +508,38 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) | |||
| 516 | } | 508 | } |
| 517 | 509 | ||
| 518 | void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { | 510 | void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { |
| 519 | auto& player = Settings::values.players.GetValue()[player_index]; | 511 | auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index); |
| 520 | 512 | ||
| 521 | const auto controller_type = GetControllerTypeFromIndex( | 513 | const auto controller_type = GetControllerTypeFromIndex( |
| 522 | emulated_controllers[player_index]->currentIndex(), player_index); | 514 | emulated_controllers[player_index]->currentIndex(), player_index); |
| 523 | const auto player_connected = player_groupboxes[player_index]->isChecked() && | 515 | const auto player_connected = player_groupboxes[player_index]->isChecked() && |
| 524 | controller_type != Settings::ControllerType::Handheld; | 516 | controller_type != Core::HID::NpadStyleIndex::Handheld; |
| 525 | 517 | ||
| 526 | if (player.controller_type == controller_type && player.connected == player_connected) { | 518 | if (controller->GetNpadStyleIndex(true) == controller_type && |
| 527 | // Set vibration devices in the event that the input device has changed. | 519 | controller->IsConnected(true) == player_connected) { |
| 528 | ConfigureVibration::SetVibrationDevices(player_index); | ||
| 529 | return; | 520 | return; |
| 530 | } | 521 | } |
| 531 | 522 | ||
| 532 | // Disconnect the controller first. | 523 | // Disconnect the controller first. |
| 533 | UpdateController(controller_type, player_index, false, system); | 524 | UpdateController(controller, controller_type, false); |
| 534 | |||
| 535 | player.controller_type = controller_type; | ||
| 536 | player.connected = player_connected; | ||
| 537 | |||
| 538 | ConfigureVibration::SetVibrationDevices(player_index); | ||
| 539 | 525 | ||
| 540 | // Handheld | 526 | // Handheld |
| 541 | if (player_index == 0) { | 527 | if (player_index == 0) { |
| 542 | auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; | 528 | if (controller_type == Core::HID::NpadStyleIndex::Handheld) { |
| 543 | if (controller_type == Settings::ControllerType::Handheld) { | 529 | auto* handheld = |
| 544 | handheld = player; | 530 | system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 531 | UpdateController(handheld, Core::HID::NpadStyleIndex::Handheld, | ||
| 532 | player_groupboxes[player_index]->isChecked()); | ||
| 545 | } | 533 | } |
| 546 | handheld.connected = player_groupboxes[player_index]->isChecked() && | ||
| 547 | controller_type == Settings::ControllerType::Handheld; | ||
| 548 | UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected, system); | ||
| 549 | } | ||
| 550 | |||
| 551 | if (!player.connected) { | ||
| 552 | return; | ||
| 553 | } | 534 | } |
| 554 | 535 | ||
| 555 | // This emulates a delay between disconnecting and reconnecting controllers as some games | 536 | UpdateController(controller, controller_type, player_connected); |
| 556 | // do not respond to a change in controller type if it was instantaneous. | ||
| 557 | using namespace std::chrono_literals; | ||
| 558 | std::this_thread::sleep_for(60ms); | ||
| 559 | |||
| 560 | UpdateController(controller_type, player_index, player_connected, system); | ||
| 561 | } | 537 | } |
| 562 | 538 | ||
| 563 | void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { | 539 | void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { |
| 564 | if (!player_groupboxes[player_index]->isChecked() || | 540 | if (!player_groupboxes[player_index]->isChecked() || |
| 565 | GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), | 541 | GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), |
| 566 | player_index) == Settings::ControllerType::Handheld) { | 542 | player_index) == Core::HID::NpadStyleIndex::Handheld) { |
| 567 | led_patterns_boxes[player_index][0]->setChecked(false); | 543 | led_patterns_boxes[player_index][0]->setChecked(false); |
| 568 | led_patterns_boxes[player_index][1]->setChecked(false); | 544 | led_patterns_boxes[player_index][1]->setChecked(false); |
| 569 | led_patterns_boxes[player_index][2]->setChecked(false); | 545 | led_patterns_boxes[player_index][2]->setChecked(false); |
| @@ -571,10 +547,12 @@ void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { | |||
| 571 | return; | 547 | return; |
| 572 | } | 548 | } |
| 573 | 549 | ||
| 574 | led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]); | 550 | const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index); |
| 575 | led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]); | 551 | const auto led_pattern = controller->GetLedPattern(); |
| 576 | led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]); | 552 | led_patterns_boxes[player_index][0]->setChecked(led_pattern.position1); |
| 577 | led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]); | 553 | led_patterns_boxes[player_index][1]->setChecked(led_pattern.position2); |
| 554 | led_patterns_boxes[player_index][2]->setChecked(led_pattern.position3); | ||
| 555 | led_patterns_boxes[player_index][3]->setChecked(led_pattern.position4); | ||
| 578 | } | 556 | } |
| 579 | 557 | ||
| 580 | void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) { | 558 | void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) { |
| @@ -654,10 +632,9 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() { | |||
| 654 | } | 632 | } |
| 655 | 633 | ||
| 656 | for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { | 634 | for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { |
| 635 | auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index); | ||
| 657 | // Disconnect any unsupported players here and disable or hide them if applicable. | 636 | // Disconnect any unsupported players here and disable or hide them if applicable. |
| 658 | Settings::values.players.GetValue()[index].connected = false; | 637 | UpdateController(controller, controller->GetNpadStyleIndex(true), false); |
| 659 | UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false, | ||
| 660 | system); | ||
| 661 | // Hide the player widgets when max_supported_controllers is less than or equal to 4. | 638 | // Hide the player widgets when max_supported_controllers is less than or equal to 4. |
| 662 | if (max_supported_players <= 4) { | 639 | if (max_supported_players <= 4) { |
| 663 | player_widgets[index]->hide(); | 640 | player_widgets[index]->hide(); |
diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h index 037325f50..7ab9ced3d 100644 --- a/src/yuzu/applets/qt_controller.h +++ b/src/yuzu/applets/qt_controller.h | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <QDialog> | 9 | #include <QDialog> |
| 10 | #include "core/core.h" | ||
| 11 | #include "core/frontend/applets/controller.h" | 10 | #include "core/frontend/applets/controller.h" |
| 12 | 11 | ||
| 13 | class GMainWindow; | 12 | class GMainWindow; |
| @@ -23,14 +22,19 @@ namespace InputCommon { | |||
| 23 | class InputSubsystem; | 22 | class InputSubsystem; |
| 24 | } | 23 | } |
| 25 | 24 | ||
| 26 | namespace Settings { | ||
| 27 | enum class ControllerType; | ||
| 28 | } | ||
| 29 | |||
| 30 | namespace Ui { | 25 | namespace Ui { |
| 31 | class QtControllerSelectorDialog; | 26 | class QtControllerSelectorDialog; |
| 32 | } | 27 | } |
| 33 | 28 | ||
| 29 | namespace Core { | ||
| 30 | class System; | ||
| 31 | } | ||
| 32 | |||
| 33 | namespace Core::HID { | ||
| 34 | class HIDCore; | ||
| 35 | enum class NpadStyleIndex : u8; | ||
| 36 | } // namespace Core::HID | ||
| 37 | |||
| 34 | class QtControllerSelectorDialog final : public QDialog { | 38 | class QtControllerSelectorDialog final : public QDialog { |
| 35 | Q_OBJECT | 39 | Q_OBJECT |
| 36 | 40 | ||
| @@ -70,10 +74,10 @@ private: | |||
| 70 | void SetEmulatedControllers(std::size_t player_index); | 74 | void SetEmulatedControllers(std::size_t player_index); |
| 71 | 75 | ||
| 72 | // Gets the Controller Type for a given controller combobox index per player. | 76 | // Gets the Controller Type for a given controller combobox index per player. |
| 73 | Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const; | 77 | Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index, std::size_t player_index) const; |
| 74 | 78 | ||
| 75 | // Gets the controller combobox index for a given Controller Type per player. | 79 | // Gets the controller combobox index for a given Controller Type per player. |
| 76 | int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const; | 80 | int GetIndexFromControllerType(Core::HID::NpadStyleIndex type, std::size_t player_index) const; |
| 77 | 81 | ||
| 78 | // Updates the controller icons per player. | 82 | // Updates the controller icons per player. |
| 79 | void UpdateControllerIcon(std::size_t player_index); | 83 | void UpdateControllerIcon(std::size_t player_index); |
| @@ -135,7 +139,7 @@ private: | |||
| 135 | std::array<QComboBox*, NUM_PLAYERS> emulated_controllers; | 139 | std::array<QComboBox*, NUM_PLAYERS> emulated_controllers; |
| 136 | 140 | ||
| 137 | /// Pairs of emulated controller index and Controller Type enum per player. | 141 | /// Pairs of emulated controller index and Controller Type enum per player. |
| 138 | std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS> | 142 | std::array<std::vector<std::pair<int, Core::HID::NpadStyleIndex>>, NUM_PLAYERS> |
| 139 | index_controller_type_pairs; | 143 | index_controller_type_pairs; |
| 140 | 144 | ||
| 141 | // Labels representing the number of connected controllers | 145 | // Labels representing the number of connected controllers |
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index a56638e21..7b19f1f8d 100644 --- a/src/yuzu/applets/qt_profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <mutex> | 5 | #include <mutex> |
| 6 | #include <QApplication> | ||
| 6 | #include <QDialogButtonBox> | 7 | #include <QDialogButtonBox> |
| 7 | #include <QHeaderView> | 8 | #include <QHeaderView> |
| 8 | #include <QLabel> | 9 | #include <QLabel> |
| @@ -16,6 +17,7 @@ | |||
| 16 | #include "core/hle/lock.h" | 17 | #include "core/hle/lock.h" |
| 17 | #include "yuzu/applets/qt_profile_select.h" | 18 | #include "yuzu/applets/qt_profile_select.h" |
| 18 | #include "yuzu/main.h" | 19 | #include "yuzu/main.h" |
| 20 | #include "yuzu/util/controller_navigation.h" | ||
| 19 | 21 | ||
| 20 | namespace { | 22 | namespace { |
| 21 | QString FormatUserEntryText(const QString& username, Common::UUID uuid) { | 23 | QString FormatUserEntryText(const QString& username, Common::UUID uuid) { |
| @@ -45,7 +47,7 @@ QPixmap GetIcon(Common::UUID uuid) { | |||
| 45 | } | 47 | } |
| 46 | } // Anonymous namespace | 48 | } // Anonymous namespace |
| 47 | 49 | ||
| 48 | QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) | 50 | QtProfileSelectionDialog::QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent) |
| 49 | : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { | 51 | : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { |
| 50 | outer_layout = new QVBoxLayout; | 52 | outer_layout = new QVBoxLayout; |
| 51 | 53 | ||
| @@ -65,6 +67,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) | |||
| 65 | tree_view = new QTreeView; | 67 | tree_view = new QTreeView; |
| 66 | item_model = new QStandardItemModel(tree_view); | 68 | item_model = new QStandardItemModel(tree_view); |
| 67 | tree_view->setModel(item_model); | 69 | tree_view->setModel(item_model); |
| 70 | controller_navigation = new ControllerNavigation(hid_core, this); | ||
| 68 | 71 | ||
| 69 | tree_view->setAlternatingRowColors(true); | 72 | tree_view->setAlternatingRowColors(true); |
| 70 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | 73 | tree_view->setSelectionMode(QHeaderView::SingleSelection); |
| @@ -91,6 +94,14 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) | |||
| 91 | scroll_area->setLayout(layout); | 94 | scroll_area->setLayout(layout); |
| 92 | 95 | ||
| 93 | connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); | 96 | connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); |
| 97 | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, | ||
| 98 | [this](Qt::Key key) { | ||
| 99 | if (!this->isActiveWindow()) { | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | ||
| 103 | QCoreApplication::postEvent(tree_view, event); | ||
| 104 | }); | ||
| 94 | 105 | ||
| 95 | const auto& profiles = profile_manager->GetAllUsers(); | 106 | const auto& profiles = profile_manager->GetAllUsers(); |
| 96 | for (const auto& user : profiles) { | 107 | for (const auto& user : profiles) { |
| @@ -113,7 +124,9 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) | |||
| 113 | resize(550, 400); | 124 | resize(550, 400); |
| 114 | } | 125 | } |
| 115 | 126 | ||
| 116 | QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; | 127 | QtProfileSelectionDialog::~QtProfileSelectionDialog() { |
| 128 | controller_navigation->UnloadController(); | ||
| 129 | }; | ||
| 117 | 130 | ||
| 118 | int QtProfileSelectionDialog::exec() { | 131 | int QtProfileSelectionDialog::exec() { |
| 119 | // Skip profile selection when there's only one. | 132 | // Skip profile selection when there's only one. |
diff --git a/src/yuzu/applets/qt_profile_select.h b/src/yuzu/applets/qt_profile_select.h index 4e9037488..56496ed31 100644 --- a/src/yuzu/applets/qt_profile_select.h +++ b/src/yuzu/applets/qt_profile_select.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "core/frontend/applets/profile_select.h" | 11 | #include "core/frontend/applets/profile_select.h" |
| 12 | #include "core/hle/service/acc/profile_manager.h" | 12 | #include "core/hle/service/acc/profile_manager.h" |
| 13 | 13 | ||
| 14 | class ControllerNavigation; | ||
| 14 | class GMainWindow; | 15 | class GMainWindow; |
| 15 | class QDialogButtonBox; | 16 | class QDialogButtonBox; |
| 16 | class QGraphicsScene; | 17 | class QGraphicsScene; |
| @@ -20,11 +21,15 @@ class QStandardItem; | |||
| 20 | class QStandardItemModel; | 21 | class QStandardItemModel; |
| 21 | class QVBoxLayout; | 22 | class QVBoxLayout; |
| 22 | 23 | ||
| 24 | namespace Core::HID { | ||
| 25 | class HIDCore; | ||
| 26 | } // namespace Core::HID | ||
| 27 | |||
| 23 | class QtProfileSelectionDialog final : public QDialog { | 28 | class QtProfileSelectionDialog final : public QDialog { |
| 24 | Q_OBJECT | 29 | Q_OBJECT |
| 25 | 30 | ||
| 26 | public: | 31 | public: |
| 27 | explicit QtProfileSelectionDialog(QWidget* parent); | 32 | explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent); |
| 28 | ~QtProfileSelectionDialog() override; | 33 | ~QtProfileSelectionDialog() override; |
| 29 | 34 | ||
| 30 | int exec() override; | 35 | int exec() override; |
| @@ -51,6 +56,7 @@ private: | |||
| 51 | QDialogButtonBox* buttons; | 56 | QDialogButtonBox* buttons; |
| 52 | 57 | ||
| 53 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; | 58 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; |
| 59 | ControllerNavigation* controller_navigation = nullptr; | ||
| 54 | }; | 60 | }; |
| 55 | 61 | ||
| 56 | class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet { | 62 | class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet { |
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index a83a11a95..c3857fc98 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp | |||
| @@ -10,7 +10,10 @@ | |||
| 10 | #include "common/settings.h" | 10 | #include "common/settings.h" |
| 11 | #include "common/string_util.h" | 11 | #include "common/string_util.h" |
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/frontend/input_interpreter.h" | 13 | #include "core/hid/emulated_controller.h" |
| 14 | #include "core/hid/hid_core.h" | ||
| 15 | #include "core/hid/hid_types.h" | ||
| 16 | #include "core/hid/input_interpreter.h" | ||
| 14 | #include "ui_qt_software_keyboard.h" | 17 | #include "ui_qt_software_keyboard.h" |
| 15 | #include "yuzu/applets/qt_software_keyboard.h" | 18 | #include "yuzu/applets/qt_software_keyboard.h" |
| 16 | #include "yuzu/main.h" | 19 | #include "yuzu/main.h" |
| @@ -472,11 +475,26 @@ void QtSoftwareKeyboardDialog::open() { | |||
| 472 | row = 0; | 475 | row = 0; |
| 473 | column = 0; | 476 | column = 0; |
| 474 | 477 | ||
| 475 | const auto* const curr_button = | 478 | switch (bottom_osk_index) { |
| 476 | keyboard_buttons[static_cast<int>(bottom_osk_index)][row][column]; | 479 | case BottomOSKIndex::LowerCase: |
| 480 | case BottomOSKIndex::UpperCase: { | ||
| 481 | const auto* const curr_button = | ||
| 482 | keyboard_buttons[static_cast<std::size_t>(bottom_osk_index)][row][column]; | ||
| 483 | |||
| 484 | // This is a workaround for setFocus() randomly not showing focus in the UI | ||
| 485 | QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); | ||
| 486 | break; | ||
| 487 | } | ||
| 488 | case BottomOSKIndex::NumberPad: { | ||
| 489 | const auto* const curr_button = numberpad_buttons[row][column]; | ||
| 477 | 490 | ||
| 478 | // This is a workaround for setFocus() randomly not showing focus in the UI | 491 | // This is a workaround for setFocus() randomly not showing focus in the UI |
| 479 | QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); | 492 | QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); |
| 493 | break; | ||
| 494 | } | ||
| 495 | default: | ||
| 496 | break; | ||
| 497 | } | ||
| 480 | 498 | ||
| 481 | StartInputThread(); | 499 | StartInputThread(); |
| 482 | } | 500 | } |
| @@ -484,7 +502,7 @@ void QtSoftwareKeyboardDialog::open() { | |||
| 484 | void QtSoftwareKeyboardDialog::reject() { | 502 | void QtSoftwareKeyboardDialog::reject() { |
| 485 | // Pressing the ESC key in a dialog calls QDialog::reject(). | 503 | // Pressing the ESC key in a dialog calls QDialog::reject(). |
| 486 | // We will override this behavior to the "Cancel" action on the software keyboard. | 504 | // We will override this behavior to the "Cancel" action on the software keyboard. |
| 487 | TranslateButtonPress(HIDButton::X); | 505 | TranslateButtonPress(Core::HID::NpadButton::X); |
| 488 | } | 506 | } |
| 489 | 507 | ||
| 490 | void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) { | 508 | void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) { |
| @@ -722,7 +740,7 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() { | |||
| 722 | 740 | ||
| 723 | connect( | 741 | connect( |
| 724 | ui->line_edit_osk, &QLineEdit::returnPressed, this, | 742 | ui->line_edit_osk, &QLineEdit::returnPressed, this, |
| 725 | [this] { TranslateButtonPress(HIDButton::Plus); }, Qt::QueuedConnection); | 743 | [this] { TranslateButtonPress(Core::HID::NpadButton::Plus); }, Qt::QueuedConnection); |
| 726 | 744 | ||
| 727 | ui->line_edit_osk->setPlaceholderText( | 745 | ui->line_edit_osk->setPlaceholderText( |
| 728 | QString::fromStdU16String(initialize_parameters.guide_text)); | 746 | QString::fromStdU16String(initialize_parameters.guide_text)); |
| @@ -795,9 +813,10 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() { | |||
| 795 | } | 813 | } |
| 796 | 814 | ||
| 797 | void QtSoftwareKeyboardDialog::SetControllerImage() { | 815 | void QtSoftwareKeyboardDialog::SetControllerImage() { |
| 798 | const auto controller_type = Settings::values.players.GetValue()[8].connected | 816 | const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 799 | ? Settings::values.players.GetValue()[8].controller_type | 817 | const auto* player_1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 800 | : Settings::values.players.GetValue()[0].controller_type; | 818 | const auto controller_type = |
| 819 | handheld->IsConnected() ? handheld->GetNpadStyleIndex() : player_1->GetNpadStyleIndex(); | ||
| 801 | 820 | ||
| 802 | const QString theme = [] { | 821 | const QString theme = [] { |
| 803 | if (QIcon::themeName().contains(QStringLiteral("dark")) || | 822 | if (QIcon::themeName().contains(QStringLiteral("dark")) || |
| @@ -809,8 +828,8 @@ void QtSoftwareKeyboardDialog::SetControllerImage() { | |||
| 809 | }(); | 828 | }(); |
| 810 | 829 | ||
| 811 | switch (controller_type) { | 830 | switch (controller_type) { |
| 812 | case Settings::ControllerType::ProController: | 831 | case Core::HID::NpadStyleIndex::ProController: |
| 813 | case Settings::ControllerType::GameCube: | 832 | case Core::HID::NpadStyleIndex::GameCube: |
| 814 | ui->icon_controller->setStyleSheet( | 833 | ui->icon_controller->setStyleSheet( |
| 815 | QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); | 834 | QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); |
| 816 | ui->icon_controller_shift->setStyleSheet( | 835 | ui->icon_controller_shift->setStyleSheet( |
| @@ -818,7 +837,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() { | |||
| 818 | ui->icon_controller_num->setStyleSheet( | 837 | ui->icon_controller_num->setStyleSheet( |
| 819 | QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); | 838 | QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); |
| 820 | break; | 839 | break; |
| 821 | case Settings::ControllerType::DualJoyconDetached: | 840 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 822 | ui->icon_controller->setStyleSheet( | 841 | ui->icon_controller->setStyleSheet( |
| 823 | QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); | 842 | QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); |
| 824 | ui->icon_controller_shift->setStyleSheet( | 843 | ui->icon_controller_shift->setStyleSheet( |
| @@ -826,7 +845,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() { | |||
| 826 | ui->icon_controller_num->setStyleSheet( | 845 | ui->icon_controller_num->setStyleSheet( |
| 827 | QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); | 846 | QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); |
| 828 | break; | 847 | break; |
| 829 | case Settings::ControllerType::LeftJoycon: | 848 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 830 | ui->icon_controller->setStyleSheet( | 849 | ui->icon_controller->setStyleSheet( |
| 831 | QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") | 850 | QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") |
| 832 | .arg(theme)); | 851 | .arg(theme)); |
| @@ -837,7 +856,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() { | |||
| 837 | QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") | 856 | QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") |
| 838 | .arg(theme)); | 857 | .arg(theme)); |
| 839 | break; | 858 | break; |
| 840 | case Settings::ControllerType::RightJoycon: | 859 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 841 | ui->icon_controller->setStyleSheet( | 860 | ui->icon_controller->setStyleSheet( |
| 842 | QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") | 861 | QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") |
| 843 | .arg(theme)); | 862 | .arg(theme)); |
| @@ -848,7 +867,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() { | |||
| 848 | QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") | 867 | QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") |
| 849 | .arg(theme)); | 868 | .arg(theme)); |
| 850 | break; | 869 | break; |
| 851 | case Settings::ControllerType::Handheld: | 870 | case Core::HID::NpadStyleIndex::Handheld: |
| 852 | ui->icon_controller->setStyleSheet( | 871 | ui->icon_controller->setStyleSheet( |
| 853 | QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); | 872 | QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); |
| 854 | ui->icon_controller_shift->setStyleSheet( | 873 | ui->icon_controller_shift->setStyleSheet( |
| @@ -1208,9 +1227,9 @@ void QtSoftwareKeyboardDialog::SetupMouseHover() { | |||
| 1208 | } | 1227 | } |
| 1209 | } | 1228 | } |
| 1210 | 1229 | ||
| 1211 | template <HIDButton... T> | 1230 | template <Core::HID::NpadButton... T> |
| 1212 | void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() { | 1231 | void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() { |
| 1213 | const auto f = [this](HIDButton button) { | 1232 | const auto f = [this](Core::HID::NpadButton button) { |
| 1214 | if (input_interpreter->IsButtonPressedOnce(button)) { | 1233 | if (input_interpreter->IsButtonPressedOnce(button)) { |
| 1215 | TranslateButtonPress(button); | 1234 | TranslateButtonPress(button); |
| 1216 | } | 1235 | } |
| @@ -1219,9 +1238,9 @@ void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() { | |||
| 1219 | (f(T), ...); | 1238 | (f(T), ...); |
| 1220 | } | 1239 | } |
| 1221 | 1240 | ||
| 1222 | template <HIDButton... T> | 1241 | template <Core::HID::NpadButton... T> |
| 1223 | void QtSoftwareKeyboardDialog::HandleButtonHold() { | 1242 | void QtSoftwareKeyboardDialog::HandleButtonHold() { |
| 1224 | const auto f = [this](HIDButton button) { | 1243 | const auto f = [this](Core::HID::NpadButton button) { |
| 1225 | if (input_interpreter->IsButtonHeld(button)) { | 1244 | if (input_interpreter->IsButtonHeld(button)) { |
| 1226 | TranslateButtonPress(button); | 1245 | TranslateButtonPress(button); |
| 1227 | } | 1246 | } |
| @@ -1230,9 +1249,9 @@ void QtSoftwareKeyboardDialog::HandleButtonHold() { | |||
| 1230 | (f(T), ...); | 1249 | (f(T), ...); |
| 1231 | } | 1250 | } |
| 1232 | 1251 | ||
| 1233 | void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { | 1252 | void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button) { |
| 1234 | switch (button) { | 1253 | switch (button) { |
| 1235 | case HIDButton::A: | 1254 | case Core::HID::NpadButton::A: |
| 1236 | switch (bottom_osk_index) { | 1255 | switch (bottom_osk_index) { |
| 1237 | case BottomOSKIndex::LowerCase: | 1256 | case BottomOSKIndex::LowerCase: |
| 1238 | case BottomOSKIndex::UpperCase: | 1257 | case BottomOSKIndex::UpperCase: |
| @@ -1245,7 +1264,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { | |||
| 1245 | break; | 1264 | break; |
| 1246 | } | 1265 | } |
| 1247 | break; | 1266 | break; |
| 1248 | case HIDButton::B: | 1267 | case Core::HID::NpadButton::B: |
| 1249 | switch (bottom_osk_index) { | 1268 | switch (bottom_osk_index) { |
| 1250 | case BottomOSKIndex::LowerCase: | 1269 | case BottomOSKIndex::LowerCase: |
| 1251 | ui->button_backspace->click(); | 1270 | ui->button_backspace->click(); |
| @@ -1260,7 +1279,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { | |||
| 1260 | break; | 1279 | break; |
| 1261 | } | 1280 | } |
| 1262 | break; | 1281 | break; |
| 1263 | case HIDButton::X: | 1282 | case Core::HID::NpadButton::X: |
| 1264 | if (is_inline) { | 1283 | if (is_inline) { |
| 1265 | emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); | 1284 | emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); |
| 1266 | } else { | 1285 | } else { |
| @@ -1271,7 +1290,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { | |||
| 1271 | emit SubmitNormalText(SwkbdResult::Cancel, std::move(text)); | 1290 | emit SubmitNormalText(SwkbdResult::Cancel, std::move(text)); |
| 1272 | } | 1291 | } |
| 1273 | break; | 1292 | break; |
| 1274 | case HIDButton::Y: | 1293 | case Core::HID::NpadButton::Y: |
| 1275 | switch (bottom_osk_index) { | 1294 | switch (bottom_osk_index) { |
| 1276 | case BottomOSKIndex::LowerCase: | 1295 | case BottomOSKIndex::LowerCase: |
| 1277 | ui->button_space->click(); | 1296 | ui->button_space->click(); |
| @@ -1284,8 +1303,8 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { | |||
| 1284 | break; | 1303 | break; |
| 1285 | } | 1304 | } |
| 1286 | break; | 1305 | break; |
| 1287 | case HIDButton::LStick: | 1306 | case Core::HID::NpadButton::StickL: |
| 1288 | case HIDButton::RStick: | 1307 | case Core::HID::NpadButton::StickR: |
| 1289 | switch (bottom_osk_index) { | 1308 | switch (bottom_osk_index) { |
| 1290 | case BottomOSKIndex::LowerCase: | 1309 | case BottomOSKIndex::LowerCase: |
| 1291 | ui->button_shift->click(); | 1310 | ui->button_shift->click(); |
| @@ -1298,13 +1317,13 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { | |||
| 1298 | break; | 1317 | break; |
| 1299 | } | 1318 | } |
| 1300 | break; | 1319 | break; |
| 1301 | case HIDButton::L: | 1320 | case Core::HID::NpadButton::L: |
| 1302 | MoveTextCursorDirection(Direction::Left); | 1321 | MoveTextCursorDirection(Direction::Left); |
| 1303 | break; | 1322 | break; |
| 1304 | case HIDButton::R: | 1323 | case Core::HID::NpadButton::R: |
| 1305 | MoveTextCursorDirection(Direction::Right); | 1324 | MoveTextCursorDirection(Direction::Right); |
| 1306 | break; | 1325 | break; |
| 1307 | case HIDButton::Plus: | 1326 | case Core::HID::NpadButton::Plus: |
| 1308 | switch (bottom_osk_index) { | 1327 | switch (bottom_osk_index) { |
| 1309 | case BottomOSKIndex::LowerCase: | 1328 | case BottomOSKIndex::LowerCase: |
| 1310 | ui->button_ok->click(); | 1329 | ui->button_ok->click(); |
| @@ -1319,24 +1338,24 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { | |||
| 1319 | break; | 1338 | break; |
| 1320 | } | 1339 | } |
| 1321 | break; | 1340 | break; |
| 1322 | case HIDButton::DLeft: | 1341 | case Core::HID::NpadButton::Left: |
| 1323 | case HIDButton::LStickLeft: | 1342 | case Core::HID::NpadButton::StickLLeft: |
| 1324 | case HIDButton::RStickLeft: | 1343 | case Core::HID::NpadButton::StickRLeft: |
| 1325 | MoveButtonDirection(Direction::Left); | 1344 | MoveButtonDirection(Direction::Left); |
| 1326 | break; | 1345 | break; |
| 1327 | case HIDButton::DUp: | 1346 | case Core::HID::NpadButton::Up: |
| 1328 | case HIDButton::LStickUp: | 1347 | case Core::HID::NpadButton::StickLUp: |
| 1329 | case HIDButton::RStickUp: | 1348 | case Core::HID::NpadButton::StickRUp: |
| 1330 | MoveButtonDirection(Direction::Up); | 1349 | MoveButtonDirection(Direction::Up); |
| 1331 | break; | 1350 | break; |
| 1332 | case HIDButton::DRight: | 1351 | case Core::HID::NpadButton::Right: |
| 1333 | case HIDButton::LStickRight: | 1352 | case Core::HID::NpadButton::StickLRight: |
| 1334 | case HIDButton::RStickRight: | 1353 | case Core::HID::NpadButton::StickRRight: |
| 1335 | MoveButtonDirection(Direction::Right); | 1354 | MoveButtonDirection(Direction::Right); |
| 1336 | break; | 1355 | break; |
| 1337 | case HIDButton::DDown: | 1356 | case Core::HID::NpadButton::Down: |
| 1338 | case HIDButton::LStickDown: | 1357 | case Core::HID::NpadButton::StickLDown: |
| 1339 | case HIDButton::RStickDown: | 1358 | case Core::HID::NpadButton::StickRDown: |
| 1340 | MoveButtonDirection(Direction::Down); | 1359 | MoveButtonDirection(Direction::Down); |
| 1341 | break; | 1360 | break; |
| 1342 | default: | 1361 | default: |
| @@ -1467,19 +1486,25 @@ void QtSoftwareKeyboardDialog::InputThread() { | |||
| 1467 | while (input_thread_running) { | 1486 | while (input_thread_running) { |
| 1468 | input_interpreter->PollInput(); | 1487 | input_interpreter->PollInput(); |
| 1469 | 1488 | ||
| 1470 | HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y, | 1489 | HandleButtonPressedOnce< |
| 1471 | HIDButton::LStick, HIDButton::RStick, HIDButton::L, HIDButton::R, | 1490 | Core::HID::NpadButton::A, Core::HID::NpadButton::B, Core::HID::NpadButton::X, |
| 1472 | HIDButton::Plus, HIDButton::DLeft, HIDButton::DUp, | 1491 | Core::HID::NpadButton::Y, Core::HID::NpadButton::StickL, Core::HID::NpadButton::StickR, |
| 1473 | HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft, | 1492 | Core::HID::NpadButton::L, Core::HID::NpadButton::R, Core::HID::NpadButton::Plus, |
| 1474 | HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown, | 1493 | Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, |
| 1475 | HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight, | 1494 | Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, |
| 1476 | HIDButton::RStickDown>(); | 1495 | Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, |
| 1477 | 1496 | Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft, | |
| 1478 | HandleButtonHold<HIDButton::B, HIDButton::L, HIDButton::R, HIDButton::DLeft, HIDButton::DUp, | 1497 | Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight, |
| 1479 | HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft, | 1498 | Core::HID::NpadButton::StickRDown>(); |
| 1480 | HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown, | 1499 | |
| 1481 | HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight, | 1500 | HandleButtonHold<Core::HID::NpadButton::B, Core::HID::NpadButton::L, |
| 1482 | HIDButton::RStickDown>(); | 1501 | Core::HID::NpadButton::R, Core::HID::NpadButton::Left, |
| 1502 | Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, | ||
| 1503 | Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, | ||
| 1504 | Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, | ||
| 1505 | Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft, | ||
| 1506 | Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight, | ||
| 1507 | Core::HID::NpadButton::StickRDown>(); | ||
| 1483 | 1508 | ||
| 1484 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); | 1509 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| 1485 | } | 1510 | } |
diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h index 592d9c085..b030cdcf7 100644 --- a/src/yuzu/applets/qt_software_keyboard.h +++ b/src/yuzu/applets/qt_software_keyboard.h | |||
| @@ -14,14 +14,16 @@ | |||
| 14 | 14 | ||
| 15 | #include "core/frontend/applets/software_keyboard.h" | 15 | #include "core/frontend/applets/software_keyboard.h" |
| 16 | 16 | ||
| 17 | enum class HIDButton : u8; | ||
| 18 | |||
| 19 | class InputInterpreter; | 17 | class InputInterpreter; |
| 20 | 18 | ||
| 21 | namespace Core { | 19 | namespace Core { |
| 22 | class System; | 20 | class System; |
| 23 | } | 21 | } |
| 24 | 22 | ||
| 23 | namespace Core::HID { | ||
| 24 | enum class NpadButton : u64; | ||
| 25 | } | ||
| 26 | |||
| 25 | namespace Ui { | 27 | namespace Ui { |
| 26 | class QtSoftwareKeyboardDialog; | 28 | class QtSoftwareKeyboardDialog; |
| 27 | } | 29 | } |
| @@ -146,7 +148,7 @@ private: | |||
| 146 | * | 148 | * |
| 147 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. | 149 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. |
| 148 | */ | 150 | */ |
| 149 | template <HIDButton... T> | 151 | template <Core::HID::NpadButton... T> |
| 150 | void HandleButtonPressedOnce(); | 152 | void HandleButtonPressedOnce(); |
| 151 | 153 | ||
| 152 | /** | 154 | /** |
| @@ -154,7 +156,7 @@ private: | |||
| 154 | * | 156 | * |
| 155 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. | 157 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. |
| 156 | */ | 158 | */ |
| 157 | template <HIDButton... T> | 159 | template <Core::HID::NpadButton... T> |
| 158 | void HandleButtonHold(); | 160 | void HandleButtonHold(); |
| 159 | 161 | ||
| 160 | /** | 162 | /** |
| @@ -162,7 +164,7 @@ private: | |||
| 162 | * | 164 | * |
| 163 | * @param button The button press to process. | 165 | * @param button The button press to process. |
| 164 | */ | 166 | */ |
| 165 | void TranslateButtonPress(HIDButton button); | 167 | void TranslateButtonPress(Core::HID::NpadButton button); |
| 166 | 168 | ||
| 167 | /** | 169 | /** |
| 168 | * Moves the focus of a button in a certain direction. | 170 | * Moves the focus of a button in a certain direction. |
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index da8c6882a..cb3c5d826 100644 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp | |||
| @@ -14,9 +14,11 @@ | |||
| 14 | #endif | 14 | #endif |
| 15 | 15 | ||
| 16 | #include "common/fs/path_util.h" | 16 | #include "common/fs/path_util.h" |
| 17 | #include "common/param_package.h" | ||
| 17 | #include "core/core.h" | 18 | #include "core/core.h" |
| 18 | #include "core/frontend/input_interpreter.h" | 19 | #include "core/hid/hid_types.h" |
| 19 | #include "input_common/keyboard.h" | 20 | #include "core/hid/input_interpreter.h" |
| 21 | #include "input_common/drivers/keyboard.h" | ||
| 20 | #include "input_common/main.h" | 22 | #include "input_common/main.h" |
| 21 | #include "yuzu/applets/qt_web_browser.h" | 23 | #include "yuzu/applets/qt_web_browser.h" |
| 22 | #include "yuzu/applets/qt_web_browser_scripts.h" | 24 | #include "yuzu/applets/qt_web_browser_scripts.h" |
| @@ -27,19 +29,19 @@ | |||
| 27 | 29 | ||
| 28 | namespace { | 30 | namespace { |
| 29 | 31 | ||
| 30 | constexpr int HIDButtonToKey(HIDButton button) { | 32 | constexpr int HIDButtonToKey(Core::HID::NpadButton button) { |
| 31 | switch (button) { | 33 | switch (button) { |
| 32 | case HIDButton::DLeft: | 34 | case Core::HID::NpadButton::Left: |
| 33 | case HIDButton::LStickLeft: | 35 | case Core::HID::NpadButton::StickLLeft: |
| 34 | return Qt::Key_Left; | 36 | return Qt::Key_Left; |
| 35 | case HIDButton::DUp: | 37 | case Core::HID::NpadButton::Up: |
| 36 | case HIDButton::LStickUp: | 38 | case Core::HID::NpadButton::StickLUp: |
| 37 | return Qt::Key_Up; | 39 | return Qt::Key_Up; |
| 38 | case HIDButton::DRight: | 40 | case Core::HID::NpadButton::Right: |
| 39 | case HIDButton::LStickRight: | 41 | case Core::HID::NpadButton::StickLRight: |
| 40 | return Qt::Key_Right; | 42 | return Qt::Key_Right; |
| 41 | case HIDButton::DDown: | 43 | case Core::HID::NpadButton::Down: |
| 42 | case HIDButton::LStickDown: | 44 | case Core::HID::NpadButton::StickLDown: |
| 43 | return Qt::Key_Down; | 45 | return Qt::Key_Down; |
| 44 | default: | 46 | default: |
| 45 | return 0; | 47 | return 0; |
| @@ -208,25 +210,25 @@ void QtNXWebEngineView::keyReleaseEvent(QKeyEvent* event) { | |||
| 208 | } | 210 | } |
| 209 | } | 211 | } |
| 210 | 212 | ||
| 211 | template <HIDButton... T> | 213 | template <Core::HID::NpadButton... T> |
| 212 | void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { | 214 | void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { |
| 213 | const auto f = [this](HIDButton button) { | 215 | const auto f = [this](Core::HID::NpadButton button) { |
| 214 | if (input_interpreter->IsButtonPressedOnce(button)) { | 216 | if (input_interpreter->IsButtonPressedOnce(button)) { |
| 215 | page()->runJavaScript( | 217 | page()->runJavaScript( |
| 216 | QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), | 218 | QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), |
| 217 | [this, button](const QVariant& variant) { | 219 | [this, button](const QVariant& variant) { |
| 218 | if (variant.toBool()) { | 220 | if (variant.toBool()) { |
| 219 | switch (button) { | 221 | switch (button) { |
| 220 | case HIDButton::A: | 222 | case Core::HID::NpadButton::A: |
| 221 | SendMultipleKeyPressEvents<Qt::Key_A, Qt::Key_Space, Qt::Key_Return>(); | 223 | SendMultipleKeyPressEvents<Qt::Key_A, Qt::Key_Space, Qt::Key_Return>(); |
| 222 | break; | 224 | break; |
| 223 | case HIDButton::B: | 225 | case Core::HID::NpadButton::B: |
| 224 | SendKeyPressEvent(Qt::Key_B); | 226 | SendKeyPressEvent(Qt::Key_B); |
| 225 | break; | 227 | break; |
| 226 | case HIDButton::X: | 228 | case Core::HID::NpadButton::X: |
| 227 | SendKeyPressEvent(Qt::Key_X); | 229 | SendKeyPressEvent(Qt::Key_X); |
| 228 | break; | 230 | break; |
| 229 | case HIDButton::Y: | 231 | case Core::HID::NpadButton::Y: |
| 230 | SendKeyPressEvent(Qt::Key_Y); | 232 | SendKeyPressEvent(Qt::Key_Y); |
| 231 | break; | 233 | break; |
| 232 | default: | 234 | default: |
| @@ -244,9 +246,9 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { | |||
| 244 | (f(T), ...); | 246 | (f(T), ...); |
| 245 | } | 247 | } |
| 246 | 248 | ||
| 247 | template <HIDButton... T> | 249 | template <Core::HID::NpadButton... T> |
| 248 | void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() { | 250 | void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() { |
| 249 | const auto f = [this](HIDButton button) { | 251 | const auto f = [this](Core::HID::NpadButton button) { |
| 250 | if (input_interpreter->IsButtonPressedOnce(button)) { | 252 | if (input_interpreter->IsButtonPressedOnce(button)) { |
| 251 | SendKeyPressEvent(HIDButtonToKey(button)); | 253 | SendKeyPressEvent(HIDButtonToKey(button)); |
| 252 | } | 254 | } |
| @@ -255,9 +257,9 @@ void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() { | |||
| 255 | (f(T), ...); | 257 | (f(T), ...); |
| 256 | } | 258 | } |
| 257 | 259 | ||
| 258 | template <HIDButton... T> | 260 | template <Core::HID::NpadButton... T> |
| 259 | void QtNXWebEngineView::HandleWindowKeyButtonHold() { | 261 | void QtNXWebEngineView::HandleWindowKeyButtonHold() { |
| 260 | const auto f = [this](HIDButton button) { | 262 | const auto f = [this](Core::HID::NpadButton button) { |
| 261 | if (input_interpreter->IsButtonHeld(button)) { | 263 | if (input_interpreter->IsButtonHeld(button)) { |
| 262 | SendKeyPressEvent(HIDButtonToKey(button)); | 264 | SendKeyPressEvent(HIDButtonToKey(button)); |
| 263 | } | 265 | } |
| @@ -308,17 +310,21 @@ void QtNXWebEngineView::InputThread() { | |||
| 308 | while (input_thread_running) { | 310 | while (input_thread_running) { |
| 309 | input_interpreter->PollInput(); | 311 | input_interpreter->PollInput(); |
| 310 | 312 | ||
| 311 | HandleWindowFooterButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y, | 313 | HandleWindowFooterButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B, |
| 312 | HIDButton::L, HIDButton::R>(); | 314 | Core::HID::NpadButton::X, Core::HID::NpadButton::Y, |
| 313 | 315 | Core::HID::NpadButton::L, Core::HID::NpadButton::R>(); | |
| 314 | HandleWindowKeyButtonPressedOnce<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight, | 316 | |
| 315 | HIDButton::DDown, HIDButton::LStickLeft, | 317 | HandleWindowKeyButtonPressedOnce< |
| 316 | HIDButton::LStickUp, HIDButton::LStickRight, | 318 | Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, |
| 317 | HIDButton::LStickDown>(); | 319 | Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, |
| 318 | 320 | Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, | |
| 319 | HandleWindowKeyButtonHold<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight, | 321 | Core::HID::NpadButton::StickLDown>(); |
| 320 | HIDButton::DDown, HIDButton::LStickLeft, HIDButton::LStickUp, | 322 | |
| 321 | HIDButton::LStickRight, HIDButton::LStickDown>(); | 323 | HandleWindowKeyButtonHold< |
| 324 | Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, | ||
| 325 | Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, | ||
| 326 | Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, | ||
| 327 | Core::HID::NpadButton::StickLDown>(); | ||
| 322 | 328 | ||
| 323 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); | 329 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| 324 | } | 330 | } |
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h index 7e9f703fc..fa18aecac 100644 --- a/src/yuzu/applets/qt_web_browser.h +++ b/src/yuzu/applets/qt_web_browser.h | |||
| @@ -16,8 +16,6 @@ | |||
| 16 | 16 | ||
| 17 | #include "core/frontend/applets/web_browser.h" | 17 | #include "core/frontend/applets/web_browser.h" |
| 18 | 18 | ||
| 19 | enum class HIDButton : u8; | ||
| 20 | |||
| 21 | class GMainWindow; | 19 | class GMainWindow; |
| 22 | class InputInterpreter; | 20 | class InputInterpreter; |
| 23 | class UrlRequestInterceptor; | 21 | class UrlRequestInterceptor; |
| @@ -26,6 +24,10 @@ namespace Core { | |||
| 26 | class System; | 24 | class System; |
| 27 | } | 25 | } |
| 28 | 26 | ||
| 27 | namespace Core::HID { | ||
| 28 | enum class NpadButton : u64; | ||
| 29 | } | ||
| 30 | |||
| 29 | namespace InputCommon { | 31 | namespace InputCommon { |
| 30 | class InputSubsystem; | 32 | class InputSubsystem; |
| 31 | } | 33 | } |
| @@ -114,7 +116,7 @@ private: | |||
| 114 | * | 116 | * |
| 115 | * @tparam HIDButton The list of buttons contained in yuzu_key_callbacks | 117 | * @tparam HIDButton The list of buttons contained in yuzu_key_callbacks |
| 116 | */ | 118 | */ |
| 117 | template <HIDButton... T> | 119 | template <Core::HID::NpadButton... T> |
| 118 | void HandleWindowFooterButtonPressedOnce(); | 120 | void HandleWindowFooterButtonPressedOnce(); |
| 119 | 121 | ||
| 120 | /** | 122 | /** |
| @@ -123,7 +125,7 @@ private: | |||
| 123 | * | 125 | * |
| 124 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. | 126 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. |
| 125 | */ | 127 | */ |
| 126 | template <HIDButton... T> | 128 | template <Core::HID::NpadButton... T> |
| 127 | void HandleWindowKeyButtonPressedOnce(); | 129 | void HandleWindowKeyButtonPressedOnce(); |
| 128 | 130 | ||
| 129 | /** | 131 | /** |
| @@ -132,7 +134,7 @@ private: | |||
| 132 | * | 134 | * |
| 133 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. | 135 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. |
| 134 | */ | 136 | */ |
| 135 | template <HIDButton... T> | 137 | template <Core::HID::NpadButton... T> |
| 136 | void HandleWindowKeyButtonHold(); | 138 | void HandleWindowKeyButtonHold(); |
| 137 | 139 | ||
| 138 | /** | 140 | /** |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 976acd176..114f17c06 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -32,10 +32,11 @@ | |||
| 32 | #include "common/settings.h" | 32 | #include "common/settings.h" |
| 33 | #include "core/core.h" | 33 | #include "core/core.h" |
| 34 | #include "core/frontend/framebuffer_layout.h" | 34 | #include "core/frontend/framebuffer_layout.h" |
| 35 | #include "input_common/keyboard.h" | 35 | #include "input_common/drivers/keyboard.h" |
| 36 | #include "input_common/drivers/mouse.h" | ||
| 37 | #include "input_common/drivers/tas_input.h" | ||
| 38 | #include "input_common/drivers/touch_screen.h" | ||
| 36 | #include "input_common/main.h" | 39 | #include "input_common/main.h" |
| 37 | #include "input_common/mouse/mouse_input.h" | ||
| 38 | #include "input_common/tas/tas_input.h" | ||
| 39 | #include "video_core/renderer_base.h" | 40 | #include "video_core/renderer_base.h" |
| 40 | #include "video_core/video_core.h" | 41 | #include "video_core/video_core.h" |
| 41 | #include "yuzu/bootmanager.h" | 42 | #include "yuzu/bootmanager.h" |
| @@ -296,13 +297,13 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, | |||
| 296 | layout->setContentsMargins(0, 0, 0, 0); | 297 | layout->setContentsMargins(0, 0, 0, 0); |
| 297 | setLayout(layout); | 298 | setLayout(layout); |
| 298 | input_subsystem->Initialize(); | 299 | input_subsystem->Initialize(); |
| 299 | |||
| 300 | this->setMouseTracking(true); | 300 | this->setMouseTracking(true); |
| 301 | 301 | ||
| 302 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | 302 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); |
| 303 | connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, | 303 | connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, |
| 304 | Qt::QueuedConnection); | 304 | Qt::QueuedConnection); |
| 305 | connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); | 305 | connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); |
| 306 | connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged); | ||
| 306 | } | 307 | } |
| 307 | 308 | ||
| 308 | void GRenderWindow::ExecuteProgram(std::size_t program_index) { | 309 | void GRenderWindow::ExecuteProgram(std::size_t program_index) { |
| @@ -319,10 +320,19 @@ GRenderWindow::~GRenderWindow() { | |||
| 319 | 320 | ||
| 320 | void GRenderWindow::OnFrameDisplayed() { | 321 | void GRenderWindow::OnFrameDisplayed() { |
| 321 | input_subsystem->GetTas()->UpdateThread(); | 322 | input_subsystem->GetTas()->UpdateThread(); |
| 323 | const InputCommon::TasInput::TasState new_tas_state = | ||
| 324 | std::get<0>(input_subsystem->GetTas()->GetStatus()); | ||
| 325 | |||
| 322 | if (!first_frame) { | 326 | if (!first_frame) { |
| 327 | last_tas_state = new_tas_state; | ||
| 323 | first_frame = true; | 328 | first_frame = true; |
| 324 | emit FirstFrameDisplayed(); | 329 | emit FirstFrameDisplayed(); |
| 325 | } | 330 | } |
| 331 | |||
| 332 | if (new_tas_state != last_tas_state) { | ||
| 333 | last_tas_state = new_tas_state; | ||
| 334 | emit TasPlaybackStateChanged(); | ||
| 335 | } | ||
| 326 | } | 336 | } |
| 327 | 337 | ||
| 328 | bool GRenderWindow::IsShown() const { | 338 | bool GRenderWindow::IsShown() const { |
| @@ -383,34 +393,329 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | |||
| 383 | QWidget::closeEvent(event); | 393 | QWidget::closeEvent(event); |
| 384 | } | 394 | } |
| 385 | 395 | ||
| 396 | int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { | ||
| 397 | switch (qt_key) { | ||
| 398 | case Qt::Key_A: | ||
| 399 | return Settings::NativeKeyboard::A; | ||
| 400 | case Qt::Key_B: | ||
| 401 | return Settings::NativeKeyboard::B; | ||
| 402 | case Qt::Key_C: | ||
| 403 | return Settings::NativeKeyboard::C; | ||
| 404 | case Qt::Key_D: | ||
| 405 | return Settings::NativeKeyboard::D; | ||
| 406 | case Qt::Key_E: | ||
| 407 | return Settings::NativeKeyboard::E; | ||
| 408 | case Qt::Key_F: | ||
| 409 | return Settings::NativeKeyboard::F; | ||
| 410 | case Qt::Key_G: | ||
| 411 | return Settings::NativeKeyboard::G; | ||
| 412 | case Qt::Key_H: | ||
| 413 | return Settings::NativeKeyboard::H; | ||
| 414 | case Qt::Key_I: | ||
| 415 | return Settings::NativeKeyboard::I; | ||
| 416 | case Qt::Key_J: | ||
| 417 | return Settings::NativeKeyboard::J; | ||
| 418 | case Qt::Key_K: | ||
| 419 | return Settings::NativeKeyboard::K; | ||
| 420 | case Qt::Key_L: | ||
| 421 | return Settings::NativeKeyboard::L; | ||
| 422 | case Qt::Key_M: | ||
| 423 | return Settings::NativeKeyboard::M; | ||
| 424 | case Qt::Key_N: | ||
| 425 | return Settings::NativeKeyboard::N; | ||
| 426 | case Qt::Key_O: | ||
| 427 | return Settings::NativeKeyboard::O; | ||
| 428 | case Qt::Key_P: | ||
| 429 | return Settings::NativeKeyboard::P; | ||
| 430 | case Qt::Key_Q: | ||
| 431 | return Settings::NativeKeyboard::Q; | ||
| 432 | case Qt::Key_R: | ||
| 433 | return Settings::NativeKeyboard::R; | ||
| 434 | case Qt::Key_S: | ||
| 435 | return Settings::NativeKeyboard::S; | ||
| 436 | case Qt::Key_T: | ||
| 437 | return Settings::NativeKeyboard::T; | ||
| 438 | case Qt::Key_U: | ||
| 439 | return Settings::NativeKeyboard::U; | ||
| 440 | case Qt::Key_V: | ||
| 441 | return Settings::NativeKeyboard::V; | ||
| 442 | case Qt::Key_W: | ||
| 443 | return Settings::NativeKeyboard::W; | ||
| 444 | case Qt::Key_X: | ||
| 445 | return Settings::NativeKeyboard::X; | ||
| 446 | case Qt::Key_Y: | ||
| 447 | return Settings::NativeKeyboard::Y; | ||
| 448 | case Qt::Key_Z: | ||
| 449 | return Settings::NativeKeyboard::Z; | ||
| 450 | case Qt::Key_1: | ||
| 451 | return Settings::NativeKeyboard::N1; | ||
| 452 | case Qt::Key_2: | ||
| 453 | return Settings::NativeKeyboard::N2; | ||
| 454 | case Qt::Key_3: | ||
| 455 | return Settings::NativeKeyboard::N3; | ||
| 456 | case Qt::Key_4: | ||
| 457 | return Settings::NativeKeyboard::N4; | ||
| 458 | case Qt::Key_5: | ||
| 459 | return Settings::NativeKeyboard::N5; | ||
| 460 | case Qt::Key_6: | ||
| 461 | return Settings::NativeKeyboard::N6; | ||
| 462 | case Qt::Key_7: | ||
| 463 | return Settings::NativeKeyboard::N7; | ||
| 464 | case Qt::Key_8: | ||
| 465 | return Settings::NativeKeyboard::N8; | ||
| 466 | case Qt::Key_9: | ||
| 467 | return Settings::NativeKeyboard::N9; | ||
| 468 | case Qt::Key_0: | ||
| 469 | return Settings::NativeKeyboard::N0; | ||
| 470 | case Qt::Key_Return: | ||
| 471 | return Settings::NativeKeyboard::Return; | ||
| 472 | case Qt::Key_Escape: | ||
| 473 | return Settings::NativeKeyboard::Escape; | ||
| 474 | case Qt::Key_Backspace: | ||
| 475 | return Settings::NativeKeyboard::Backspace; | ||
| 476 | case Qt::Key_Tab: | ||
| 477 | return Settings::NativeKeyboard::Tab; | ||
| 478 | case Qt::Key_Space: | ||
| 479 | return Settings::NativeKeyboard::Space; | ||
| 480 | case Qt::Key_Minus: | ||
| 481 | return Settings::NativeKeyboard::Minus; | ||
| 482 | case Qt::Key_Plus: | ||
| 483 | case Qt::Key_questiondown: | ||
| 484 | return Settings::NativeKeyboard::Plus; | ||
| 485 | case Qt::Key_BracketLeft: | ||
| 486 | case Qt::Key_BraceLeft: | ||
| 487 | return Settings::NativeKeyboard::OpenBracket; | ||
| 488 | case Qt::Key_BracketRight: | ||
| 489 | case Qt::Key_BraceRight: | ||
| 490 | return Settings::NativeKeyboard::CloseBracket; | ||
| 491 | case Qt::Key_Bar: | ||
| 492 | return Settings::NativeKeyboard::Pipe; | ||
| 493 | case Qt::Key_Dead_Tilde: | ||
| 494 | return Settings::NativeKeyboard::Tilde; | ||
| 495 | case Qt::Key_Ntilde: | ||
| 496 | case Qt::Key_Semicolon: | ||
| 497 | return Settings::NativeKeyboard::Semicolon; | ||
| 498 | case Qt::Key_Apostrophe: | ||
| 499 | return Settings::NativeKeyboard::Quote; | ||
| 500 | case Qt::Key_Dead_Grave: | ||
| 501 | return Settings::NativeKeyboard::Backquote; | ||
| 502 | case Qt::Key_Comma: | ||
| 503 | return Settings::NativeKeyboard::Comma; | ||
| 504 | case Qt::Key_Period: | ||
| 505 | return Settings::NativeKeyboard::Period; | ||
| 506 | case Qt::Key_Slash: | ||
| 507 | return Settings::NativeKeyboard::Slash; | ||
| 508 | case Qt::Key_CapsLock: | ||
| 509 | return Settings::NativeKeyboard::CapsLock; | ||
| 510 | case Qt::Key_F1: | ||
| 511 | return Settings::NativeKeyboard::F1; | ||
| 512 | case Qt::Key_F2: | ||
| 513 | return Settings::NativeKeyboard::F2; | ||
| 514 | case Qt::Key_F3: | ||
| 515 | return Settings::NativeKeyboard::F3; | ||
| 516 | case Qt::Key_F4: | ||
| 517 | return Settings::NativeKeyboard::F4; | ||
| 518 | case Qt::Key_F5: | ||
| 519 | return Settings::NativeKeyboard::F5; | ||
| 520 | case Qt::Key_F6: | ||
| 521 | return Settings::NativeKeyboard::F6; | ||
| 522 | case Qt::Key_F7: | ||
| 523 | return Settings::NativeKeyboard::F7; | ||
| 524 | case Qt::Key_F8: | ||
| 525 | return Settings::NativeKeyboard::F8; | ||
| 526 | case Qt::Key_F9: | ||
| 527 | return Settings::NativeKeyboard::F9; | ||
| 528 | case Qt::Key_F10: | ||
| 529 | return Settings::NativeKeyboard::F10; | ||
| 530 | case Qt::Key_F11: | ||
| 531 | return Settings::NativeKeyboard::F11; | ||
| 532 | case Qt::Key_F12: | ||
| 533 | return Settings::NativeKeyboard::F12; | ||
| 534 | case Qt::Key_Print: | ||
| 535 | return Settings::NativeKeyboard::PrintScreen; | ||
| 536 | case Qt::Key_ScrollLock: | ||
| 537 | return Settings::NativeKeyboard::ScrollLock; | ||
| 538 | case Qt::Key_Pause: | ||
| 539 | return Settings::NativeKeyboard::Pause; | ||
| 540 | case Qt::Key_Insert: | ||
| 541 | return Settings::NativeKeyboard::Insert; | ||
| 542 | case Qt::Key_Home: | ||
| 543 | return Settings::NativeKeyboard::Home; | ||
| 544 | case Qt::Key_PageUp: | ||
| 545 | return Settings::NativeKeyboard::PageUp; | ||
| 546 | case Qt::Key_Delete: | ||
| 547 | return Settings::NativeKeyboard::Delete; | ||
| 548 | case Qt::Key_End: | ||
| 549 | return Settings::NativeKeyboard::End; | ||
| 550 | case Qt::Key_PageDown: | ||
| 551 | return Settings::NativeKeyboard::PageDown; | ||
| 552 | case Qt::Key_Right: | ||
| 553 | return Settings::NativeKeyboard::Right; | ||
| 554 | case Qt::Key_Left: | ||
| 555 | return Settings::NativeKeyboard::Left; | ||
| 556 | case Qt::Key_Down: | ||
| 557 | return Settings::NativeKeyboard::Down; | ||
| 558 | case Qt::Key_Up: | ||
| 559 | return Settings::NativeKeyboard::Up; | ||
| 560 | case Qt::Key_NumLock: | ||
| 561 | return Settings::NativeKeyboard::NumLock; | ||
| 562 | // Numpad keys are missing here | ||
| 563 | case Qt::Key_F13: | ||
| 564 | return Settings::NativeKeyboard::F13; | ||
| 565 | case Qt::Key_F14: | ||
| 566 | return Settings::NativeKeyboard::F14; | ||
| 567 | case Qt::Key_F15: | ||
| 568 | return Settings::NativeKeyboard::F15; | ||
| 569 | case Qt::Key_F16: | ||
| 570 | return Settings::NativeKeyboard::F16; | ||
| 571 | case Qt::Key_F17: | ||
| 572 | return Settings::NativeKeyboard::F17; | ||
| 573 | case Qt::Key_F18: | ||
| 574 | return Settings::NativeKeyboard::F18; | ||
| 575 | case Qt::Key_F19: | ||
| 576 | return Settings::NativeKeyboard::F19; | ||
| 577 | case Qt::Key_F20: | ||
| 578 | return Settings::NativeKeyboard::F20; | ||
| 579 | case Qt::Key_F21: | ||
| 580 | return Settings::NativeKeyboard::F21; | ||
| 581 | case Qt::Key_F22: | ||
| 582 | return Settings::NativeKeyboard::F22; | ||
| 583 | case Qt::Key_F23: | ||
| 584 | return Settings::NativeKeyboard::F23; | ||
| 585 | case Qt::Key_F24: | ||
| 586 | return Settings::NativeKeyboard::F24; | ||
| 587 | // case Qt::: | ||
| 588 | // return Settings::NativeKeyboard::KPComma; | ||
| 589 | // case Qt::: | ||
| 590 | // return Settings::NativeKeyboard::Ro; | ||
| 591 | case Qt::Key_Hiragana_Katakana: | ||
| 592 | return Settings::NativeKeyboard::KatakanaHiragana; | ||
| 593 | case Qt::Key_yen: | ||
| 594 | return Settings::NativeKeyboard::Yen; | ||
| 595 | case Qt::Key_Henkan: | ||
| 596 | return Settings::NativeKeyboard::Henkan; | ||
| 597 | case Qt::Key_Muhenkan: | ||
| 598 | return Settings::NativeKeyboard::Muhenkan; | ||
| 599 | // case Qt::: | ||
| 600 | // return Settings::NativeKeyboard::NumPadCommaPc98; | ||
| 601 | case Qt::Key_Hangul: | ||
| 602 | return Settings::NativeKeyboard::HangulEnglish; | ||
| 603 | case Qt::Key_Hangul_Hanja: | ||
| 604 | return Settings::NativeKeyboard::Hanja; | ||
| 605 | case Qt::Key_Katakana: | ||
| 606 | return Settings::NativeKeyboard::KatakanaKey; | ||
| 607 | case Qt::Key_Hiragana: | ||
| 608 | return Settings::NativeKeyboard::HiraganaKey; | ||
| 609 | case Qt::Key_Zenkaku_Hankaku: | ||
| 610 | return Settings::NativeKeyboard::ZenkakuHankaku; | ||
| 611 | // Modifier keys are handled by the modifier property | ||
| 612 | default: | ||
| 613 | return Settings::NativeKeyboard::None; | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) { | ||
| 618 | int modifier = 0; | ||
| 619 | |||
| 620 | if ((qt_modifiers & Qt::KeyboardModifier::ShiftModifier) != 0) { | ||
| 621 | modifier |= 1 << Settings::NativeKeyboard::LeftShift; | ||
| 622 | } | ||
| 623 | if ((qt_modifiers & Qt::KeyboardModifier::ControlModifier) != 0) { | ||
| 624 | modifier |= 1 << Settings::NativeKeyboard::LeftControl; | ||
| 625 | } | ||
| 626 | if ((qt_modifiers & Qt::KeyboardModifier::AltModifier) != 0) { | ||
| 627 | modifier |= 1 << Settings::NativeKeyboard::LeftAlt; | ||
| 628 | } | ||
| 629 | if ((qt_modifiers & Qt::KeyboardModifier::MetaModifier) != 0) { | ||
| 630 | modifier |= 1 << Settings::NativeKeyboard::LeftMeta; | ||
| 631 | } | ||
| 632 | |||
| 633 | // TODO: These keys can't be obtained with Qt::KeyboardModifier | ||
| 634 | |||
| 635 | // if ((qt_modifiers & 0x10) != 0) { | ||
| 636 | // modifier |= 1 << Settings::NativeKeyboard::RightShift; | ||
| 637 | // } | ||
| 638 | // if ((qt_modifiers & 0x20) != 0) { | ||
| 639 | // modifier |= 1 << Settings::NativeKeyboard::RightControl; | ||
| 640 | // } | ||
| 641 | // if ((qt_modifiers & 0x40) != 0) { | ||
| 642 | // modifier |= 1 << Settings::NativeKeyboard::RightAlt; | ||
| 643 | // } | ||
| 644 | // if ((qt_modifiers & 0x80) != 0) { | ||
| 645 | // modifier |= 1 << Settings::NativeKeyboard::RightMeta; | ||
| 646 | // } | ||
| 647 | // if ((qt_modifiers & 0x100) != 0) { | ||
| 648 | // modifier |= 1 << Settings::NativeKeyboard::CapsLock; | ||
| 649 | // } | ||
| 650 | // if ((qt_modifiers & 0x200) != 0) { | ||
| 651 | // modifier |= 1 << Settings::NativeKeyboard::NumLock; | ||
| 652 | // } | ||
| 653 | // if ((qt_modifiers & ???) != 0) { | ||
| 654 | // modifier |= 1 << Settings::NativeKeyboard::ScrollLock; | ||
| 655 | // } | ||
| 656 | // if ((qt_modifiers & ???) != 0) { | ||
| 657 | // modifier |= 1 << Settings::NativeKeyboard::Katakana; | ||
| 658 | // } | ||
| 659 | // if ((qt_modifiers & ???) != 0) { | ||
| 660 | // modifier |= 1 << Settings::NativeKeyboard::Hiragana; | ||
| 661 | // } | ||
| 662 | return modifier; | ||
| 663 | } | ||
| 664 | |||
| 386 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | 665 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { |
| 666 | /** | ||
| 667 | * This feature can be enhanced with the following functions, but they do not provide | ||
| 668 | * cross-platform behavior. | ||
| 669 | * | ||
| 670 | * event->nativeVirtualKey() can distinguish between keys on the numpad. | ||
| 671 | * event->nativeModifiers() can distinguish between left and right keys and numlock, | ||
| 672 | * capslock, scroll lock. | ||
| 673 | */ | ||
| 387 | if (!event->isAutoRepeat()) { | 674 | if (!event->isAutoRepeat()) { |
| 675 | const auto modifier = QtModifierToSwitchModifier(event->modifiers()); | ||
| 676 | const auto key = QtKeyToSwitchKey(Qt::Key(event->key())); | ||
| 677 | input_subsystem->GetKeyboard()->SetKeyboardModifiers(modifier); | ||
| 678 | input_subsystem->GetKeyboard()->PressKeyboardKey(key); | ||
| 679 | // This is used for gamepads that can have any key mapped | ||
| 388 | input_subsystem->GetKeyboard()->PressKey(event->key()); | 680 | input_subsystem->GetKeyboard()->PressKey(event->key()); |
| 389 | } | 681 | } |
| 390 | } | 682 | } |
| 391 | 683 | ||
| 392 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | 684 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { |
| 685 | /** | ||
| 686 | * This feature can be enhanced with the following functions, but they do not provide | ||
| 687 | * cross-platform behavior. | ||
| 688 | * | ||
| 689 | * event->nativeVirtualKey() can distinguish between keys on the numpad. | ||
| 690 | * event->nativeModifiers() can distinguish between left and right buttons and numlock, | ||
| 691 | * capslock, scroll lock. | ||
| 692 | */ | ||
| 393 | if (!event->isAutoRepeat()) { | 693 | if (!event->isAutoRepeat()) { |
| 694 | const auto modifier = QtModifierToSwitchModifier(event->modifiers()); | ||
| 695 | const auto key = QtKeyToSwitchKey(Qt::Key(event->key())); | ||
| 696 | input_subsystem->GetKeyboard()->SetKeyboardModifiers(modifier); | ||
| 697 | input_subsystem->GetKeyboard()->ReleaseKeyboardKey(key); | ||
| 698 | // This is used for gamepads that can have any key mapped | ||
| 394 | input_subsystem->GetKeyboard()->ReleaseKey(event->key()); | 699 | input_subsystem->GetKeyboard()->ReleaseKey(event->key()); |
| 395 | } | 700 | } |
| 396 | } | 701 | } |
| 397 | 702 | ||
| 398 | MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) { | 703 | InputCommon::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) { |
| 399 | switch (button) { | 704 | switch (button) { |
| 400 | case Qt::LeftButton: | 705 | case Qt::LeftButton: |
| 401 | return MouseInput::MouseButton::Left; | 706 | return InputCommon::MouseButton::Left; |
| 402 | case Qt::RightButton: | 707 | case Qt::RightButton: |
| 403 | return MouseInput::MouseButton::Right; | 708 | return InputCommon::MouseButton::Right; |
| 404 | case Qt::MiddleButton: | 709 | case Qt::MiddleButton: |
| 405 | return MouseInput::MouseButton::Wheel; | 710 | return InputCommon::MouseButton::Wheel; |
| 406 | case Qt::BackButton: | 711 | case Qt::BackButton: |
| 407 | return MouseInput::MouseButton::Backward; | 712 | return InputCommon::MouseButton::Backward; |
| 408 | case Qt::ForwardButton: | 713 | case Qt::ForwardButton: |
| 409 | return MouseInput::MouseButton::Forward; | 714 | return InputCommon::MouseButton::Forward; |
| 410 | case Qt::TaskButton: | 715 | case Qt::TaskButton: |
| 411 | return MouseInput::MouseButton::Task; | 716 | return InputCommon::MouseButton::Task; |
| 412 | default: | 717 | default: |
| 413 | return MouseInput::MouseButton::Extra; | 718 | return InputCommon::MouseButton::Extra; |
| 414 | } | 719 | } |
| 415 | } | 720 | } |
| 416 | 721 | ||
| @@ -423,12 +728,9 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { | |||
| 423 | // coordinates and map them to the current render area | 728 | // coordinates and map them to the current render area |
| 424 | const auto pos = mapFromGlobal(QCursor::pos()); | 729 | const auto pos = mapFromGlobal(QCursor::pos()); |
| 425 | const auto [x, y] = ScaleTouch(pos); | 730 | const auto [x, y] = ScaleTouch(pos); |
| 731 | const auto [touch_x, touch_y] = MapToTouchScreen(x, y); | ||
| 426 | const auto button = QtButtonToMouseButton(event->button()); | 732 | const auto button = QtButtonToMouseButton(event->button()); |
| 427 | input_subsystem->GetMouse()->PressButton(x, y, button); | 733 | input_subsystem->GetMouse()->PressButton(x, y, touch_x, touch_y, button); |
| 428 | |||
| 429 | if (event->button() == Qt::LeftButton) { | ||
| 430 | this->TouchPressed(x, y, 0); | ||
| 431 | } | ||
| 432 | 734 | ||
| 433 | emit MouseActivity(); | 735 | emit MouseActivity(); |
| 434 | } | 736 | } |
| @@ -442,12 +744,12 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| 442 | // coordinates and map them to the current render area | 744 | // coordinates and map them to the current render area |
| 443 | const auto pos = mapFromGlobal(QCursor::pos()); | 745 | const auto pos = mapFromGlobal(QCursor::pos()); |
| 444 | const auto [x, y] = ScaleTouch(pos); | 746 | const auto [x, y] = ScaleTouch(pos); |
| 747 | const auto [touch_x, touch_y] = MapToTouchScreen(x, y); | ||
| 445 | const int center_x = width() / 2; | 748 | const int center_x = width() / 2; |
| 446 | const int center_y = height() / 2; | 749 | const int center_y = height() / 2; |
| 447 | input_subsystem->GetMouse()->MouseMove(x, y, center_x, center_y); | 750 | input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y); |
| 448 | this->TouchMoved(x, y, 0); | ||
| 449 | 751 | ||
| 450 | if (Settings::values.mouse_panning) { | 752 | if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { |
| 451 | QCursor::setPos(mapToGlobal({center_x, center_y})); | 753 | QCursor::setPos(mapToGlobal({center_x, center_y})); |
| 452 | } | 754 | } |
| 453 | 755 | ||
| @@ -462,10 +764,12 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | |||
| 462 | 764 | ||
| 463 | const auto button = QtButtonToMouseButton(event->button()); | 765 | const auto button = QtButtonToMouseButton(event->button()); |
| 464 | input_subsystem->GetMouse()->ReleaseButton(button); | 766 | input_subsystem->GetMouse()->ReleaseButton(button); |
| 767 | } | ||
| 465 | 768 | ||
| 466 | if (event->button() == Qt::LeftButton) { | 769 | void GRenderWindow::wheelEvent(QWheelEvent* event) { |
| 467 | this->TouchReleased(0); | 770 | const int x = event->angleDelta().x(); |
| 468 | } | 771 | const int y = event->angleDelta().y(); |
| 772 | input_subsystem->GetMouse()->MouseWheelChange(x, y); | ||
| 469 | } | 773 | } |
| 470 | 774 | ||
| 471 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 775 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| @@ -488,7 +792,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { | |||
| 488 | for (std::size_t id = 0; id < touch_ids.size(); ++id) { | 792 | for (std::size_t id = 0; id < touch_ids.size(); ++id) { |
| 489 | if (!TouchExist(touch_ids[id], touch_points)) { | 793 | if (!TouchExist(touch_ids[id], touch_points)) { |
| 490 | touch_ids[id] = 0; | 794 | touch_ids[id] = 0; |
| 491 | this->TouchReleased(id + 1); | 795 | input_subsystem->GetTouchScreen()->TouchReleased(id); |
| 492 | } | 796 | } |
| 493 | } | 797 | } |
| 494 | } | 798 | } |
| @@ -497,28 +801,28 @@ void GRenderWindow::TouchEndEvent() { | |||
| 497 | for (std::size_t id = 0; id < touch_ids.size(); ++id) { | 801 | for (std::size_t id = 0; id < touch_ids.size(); ++id) { |
| 498 | if (touch_ids[id] != 0) { | 802 | if (touch_ids[id] != 0) { |
| 499 | touch_ids[id] = 0; | 803 | touch_ids[id] = 0; |
| 500 | this->TouchReleased(id + 1); | 804 | input_subsystem->GetTouchScreen()->TouchReleased(id); |
| 501 | } | 805 | } |
| 502 | } | 806 | } |
| 503 | } | 807 | } |
| 504 | 808 | ||
| 505 | bool GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) { | 809 | void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) { |
| 506 | for (std::size_t id = 0; id < touch_ids.size(); ++id) { | 810 | for (std::size_t id = 0; id < touch_ids.size(); ++id) { |
| 507 | if (touch_ids[id] == 0) { | 811 | if (touch_ids[id] == 0) { |
| 508 | touch_ids[id] = touch_point.id() + 1; | 812 | touch_ids[id] = touch_point.id() + 1; |
| 509 | const auto [x, y] = ScaleTouch(touch_point.pos()); | 813 | const auto [x, y] = ScaleTouch(touch_point.pos()); |
| 510 | this->TouchPressed(x, y, id + 1); | 814 | const auto [touch_x, touch_y] = MapToTouchScreen(x, y); |
| 511 | return true; | 815 | input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id); |
| 512 | } | 816 | } |
| 513 | } | 817 | } |
| 514 | return false; | ||
| 515 | } | 818 | } |
| 516 | 819 | ||
| 517 | bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) { | 820 | bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) { |
| 518 | for (std::size_t id = 0; id < touch_ids.size(); ++id) { | 821 | for (std::size_t id = 0; id < touch_ids.size(); ++id) { |
| 519 | if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) { | 822 | if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) { |
| 520 | const auto [x, y] = ScaleTouch(touch_point.pos()); | 823 | const auto [x, y] = ScaleTouch(touch_point.pos()); |
| 521 | this->TouchMoved(x, y, id + 1); | 824 | const auto [touch_x, touch_y] = MapToTouchScreen(x, y); |
| 825 | input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id); | ||
| 522 | return true; | 826 | return true; |
| 523 | } | 827 | } |
| 524 | } | 828 | } |
| @@ -551,7 +855,7 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) { | |||
| 551 | QWidget::focusOutEvent(event); | 855 | QWidget::focusOutEvent(event); |
| 552 | input_subsystem->GetKeyboard()->ReleaseAllKeys(); | 856 | input_subsystem->GetKeyboard()->ReleaseAllKeys(); |
| 553 | input_subsystem->GetMouse()->ReleaseAllButtons(); | 857 | input_subsystem->GetMouse()->ReleaseAllButtons(); |
| 554 | this->TouchReleased(0); | 858 | input_subsystem->GetTouchScreen()->ReleaseAllTouch(); |
| 555 | } | 859 | } |
| 556 | 860 | ||
| 557 | void GRenderWindow::resizeEvent(QResizeEvent* event) { | 861 | void GRenderWindow::resizeEvent(QResizeEvent* event) { |
| @@ -630,7 +934,7 @@ void GRenderWindow::ReleaseRenderTarget() { | |||
| 630 | 934 | ||
| 631 | void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) { | 935 | void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) { |
| 632 | auto& renderer = system.Renderer(); | 936 | auto& renderer = system.Renderer(); |
| 633 | const f32 res_scale = VideoCore::GetResolutionScaleFactor(renderer); | 937 | const f32 res_scale = Settings::values.resolution_info.up_factor; |
| 634 | 938 | ||
| 635 | const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; | 939 | const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; |
| 636 | screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); | 940 | screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); |
| @@ -760,7 +1064,7 @@ void GRenderWindow::showEvent(QShowEvent* event) { | |||
| 760 | 1064 | ||
| 761 | bool GRenderWindow::eventFilter(QObject* object, QEvent* event) { | 1065 | bool GRenderWindow::eventFilter(QObject* object, QEvent* event) { |
| 762 | if (event->type() == QEvent::HoverMove) { | 1066 | if (event->type() == QEvent::HoverMove) { |
| 763 | if (Settings::values.mouse_panning) { | 1067 | if (Settings::values.mouse_panning || Settings::values.mouse_enabled) { |
| 764 | auto* hover_event = static_cast<QMouseEvent*>(event); | 1068 | auto* hover_event = static_cast<QMouseEvent*>(event); |
| 765 | mouseMoveEvent(hover_event); | 1069 | mouseMoveEvent(hover_event); |
| 766 | return false; | 1070 | return false; |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 40fd4a9d6..92297a43b 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -30,11 +30,12 @@ class System; | |||
| 30 | 30 | ||
| 31 | namespace InputCommon { | 31 | namespace InputCommon { |
| 32 | class InputSubsystem; | 32 | class InputSubsystem; |
| 33 | } | ||
| 34 | |||
| 35 | namespace MouseInput { | ||
| 36 | enum class MouseButton; | 33 | enum class MouseButton; |
| 37 | } | 34 | } // namespace InputCommon |
| 35 | |||
| 36 | namespace InputCommon::TasInput { | ||
| 37 | enum class TasState; | ||
| 38 | } // namespace InputCommon::TasInput | ||
| 38 | 39 | ||
| 39 | namespace VideoCore { | 40 | namespace VideoCore { |
| 40 | enum class LoadCallbackStage; | 41 | enum class LoadCallbackStage; |
| @@ -157,15 +158,22 @@ public: | |||
| 157 | 158 | ||
| 158 | void resizeEvent(QResizeEvent* event) override; | 159 | void resizeEvent(QResizeEvent* event) override; |
| 159 | 160 | ||
| 161 | /// Converts a Qt keybard key into NativeKeyboard key | ||
| 162 | static int QtKeyToSwitchKey(Qt::Key qt_keys); | ||
| 163 | |||
| 164 | /// Converts a Qt modifier keys into NativeKeyboard modifier keys | ||
| 165 | static int QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers); | ||
| 166 | |||
| 160 | void keyPressEvent(QKeyEvent* event) override; | 167 | void keyPressEvent(QKeyEvent* event) override; |
| 161 | void keyReleaseEvent(QKeyEvent* event) override; | 168 | void keyReleaseEvent(QKeyEvent* event) override; |
| 162 | 169 | ||
| 163 | /// Converts a Qt mouse button into MouseInput mouse button | 170 | /// Converts a Qt mouse button into MouseInput mouse button |
| 164 | static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button); | 171 | static InputCommon::MouseButton QtButtonToMouseButton(Qt::MouseButton button); |
| 165 | 172 | ||
| 166 | void mousePressEvent(QMouseEvent* event) override; | 173 | void mousePressEvent(QMouseEvent* event) override; |
| 167 | void mouseMoveEvent(QMouseEvent* event) override; | 174 | void mouseMoveEvent(QMouseEvent* event) override; |
| 168 | void mouseReleaseEvent(QMouseEvent* event) override; | 175 | void mouseReleaseEvent(QMouseEvent* event) override; |
| 176 | void wheelEvent(QWheelEvent* event) override; | ||
| 169 | 177 | ||
| 170 | bool event(QEvent* event) override; | 178 | bool event(QEvent* event) override; |
| 171 | 179 | ||
| @@ -203,13 +211,14 @@ signals: | |||
| 203 | void ExecuteProgramSignal(std::size_t program_index); | 211 | void ExecuteProgramSignal(std::size_t program_index); |
| 204 | void ExitSignal(); | 212 | void ExitSignal(); |
| 205 | void MouseActivity(); | 213 | void MouseActivity(); |
| 214 | void TasPlaybackStateChanged(); | ||
| 206 | 215 | ||
| 207 | private: | 216 | private: |
| 208 | void TouchBeginEvent(const QTouchEvent* event); | 217 | void TouchBeginEvent(const QTouchEvent* event); |
| 209 | void TouchUpdateEvent(const QTouchEvent* event); | 218 | void TouchUpdateEvent(const QTouchEvent* event); |
| 210 | void TouchEndEvent(); | 219 | void TouchEndEvent(); |
| 211 | 220 | ||
| 212 | bool TouchStart(const QTouchEvent::TouchPoint& touch_point); | 221 | void TouchStart(const QTouchEvent::TouchPoint& touch_point); |
| 213 | bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point); | 222 | bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point); |
| 214 | bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const; | 223 | bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const; |
| 215 | 224 | ||
| @@ -236,6 +245,7 @@ private: | |||
| 236 | QWidget* child_widget = nullptr; | 245 | QWidget* child_widget = nullptr; |
| 237 | 246 | ||
| 238 | bool first_frame = false; | 247 | bool first_frame = false; |
| 248 | InputCommon::TasInput::TasState last_tas_state; | ||
| 239 | 249 | ||
| 240 | std::array<std::size_t, 16> touch_ids{}; | 250 | std::array<std::size_t, 16> touch_ids{}; |
| 241 | 251 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 8227d06bc..0f679c37e 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include "core/hle/service/acc/profile_manager.h" | 11 | #include "core/hle/service/acc/profile_manager.h" |
| 12 | #include "core/hle/service/hid/controllers/npad.h" | 12 | #include "core/hle/service/hid/controllers/npad.h" |
| 13 | #include "input_common/main.h" | 13 | #include "input_common/main.h" |
| 14 | #include "input_common/udp/client.h" | ||
| 15 | #include "yuzu/configuration/config.h" | 14 | #include "yuzu/configuration/config.h" |
| 16 | 15 | ||
| 17 | namespace FS = Common::FS; | 16 | namespace FS = Common::FS; |
| @@ -61,162 +60,6 @@ const std::array<int, 2> Config::default_stick_mod = { | |||
| 61 | 0, | 60 | 0, |
| 62 | }; | 61 | }; |
| 63 | 62 | ||
| 64 | const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons = | ||
| 65 | { | ||
| 66 | Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal, | ||
| 67 | }; | ||
| 68 | |||
| 69 | const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = { | ||
| 70 | 0, | ||
| 71 | 0, | ||
| 72 | 0, | ||
| 73 | 0, | ||
| 74 | Qt::Key_A, | ||
| 75 | Qt::Key_B, | ||
| 76 | Qt::Key_C, | ||
| 77 | Qt::Key_D, | ||
| 78 | Qt::Key_E, | ||
| 79 | Qt::Key_F, | ||
| 80 | Qt::Key_G, | ||
| 81 | Qt::Key_H, | ||
| 82 | Qt::Key_I, | ||
| 83 | Qt::Key_J, | ||
| 84 | Qt::Key_K, | ||
| 85 | Qt::Key_L, | ||
| 86 | Qt::Key_M, | ||
| 87 | Qt::Key_N, | ||
| 88 | Qt::Key_O, | ||
| 89 | Qt::Key_P, | ||
| 90 | Qt::Key_Q, | ||
| 91 | Qt::Key_R, | ||
| 92 | Qt::Key_S, | ||
| 93 | Qt::Key_T, | ||
| 94 | Qt::Key_U, | ||
| 95 | Qt::Key_V, | ||
| 96 | Qt::Key_W, | ||
| 97 | Qt::Key_X, | ||
| 98 | Qt::Key_Y, | ||
| 99 | Qt::Key_Z, | ||
| 100 | Qt::Key_1, | ||
| 101 | Qt::Key_2, | ||
| 102 | Qt::Key_3, | ||
| 103 | Qt::Key_4, | ||
| 104 | Qt::Key_5, | ||
| 105 | Qt::Key_6, | ||
| 106 | Qt::Key_7, | ||
| 107 | Qt::Key_8, | ||
| 108 | Qt::Key_9, | ||
| 109 | Qt::Key_0, | ||
| 110 | Qt::Key_Enter, | ||
| 111 | Qt::Key_Escape, | ||
| 112 | Qt::Key_Backspace, | ||
| 113 | Qt::Key_Tab, | ||
| 114 | Qt::Key_Space, | ||
| 115 | Qt::Key_Minus, | ||
| 116 | Qt::Key_Equal, | ||
| 117 | Qt::Key_BracketLeft, | ||
| 118 | Qt::Key_BracketRight, | ||
| 119 | Qt::Key_Backslash, | ||
| 120 | Qt::Key_Dead_Tilde, | ||
| 121 | Qt::Key_Semicolon, | ||
| 122 | Qt::Key_Apostrophe, | ||
| 123 | Qt::Key_Dead_Grave, | ||
| 124 | Qt::Key_Comma, | ||
| 125 | Qt::Key_Period, | ||
| 126 | Qt::Key_Slash, | ||
| 127 | Qt::Key_CapsLock, | ||
| 128 | |||
| 129 | Qt::Key_F1, | ||
| 130 | Qt::Key_F2, | ||
| 131 | Qt::Key_F3, | ||
| 132 | Qt::Key_F4, | ||
| 133 | Qt::Key_F5, | ||
| 134 | Qt::Key_F6, | ||
| 135 | Qt::Key_F7, | ||
| 136 | Qt::Key_F8, | ||
| 137 | Qt::Key_F9, | ||
| 138 | Qt::Key_F10, | ||
| 139 | Qt::Key_F11, | ||
| 140 | Qt::Key_F12, | ||
| 141 | |||
| 142 | Qt::Key_SysReq, | ||
| 143 | Qt::Key_ScrollLock, | ||
| 144 | Qt::Key_Pause, | ||
| 145 | Qt::Key_Insert, | ||
| 146 | Qt::Key_Home, | ||
| 147 | Qt::Key_PageUp, | ||
| 148 | Qt::Key_Delete, | ||
| 149 | Qt::Key_End, | ||
| 150 | Qt::Key_PageDown, | ||
| 151 | Qt::Key_Right, | ||
| 152 | Qt::Key_Left, | ||
| 153 | Qt::Key_Down, | ||
| 154 | Qt::Key_Up, | ||
| 155 | |||
| 156 | Qt::Key_NumLock, | ||
| 157 | Qt::Key_Slash, | ||
| 158 | Qt::Key_Asterisk, | ||
| 159 | Qt::Key_Minus, | ||
| 160 | Qt::Key_Plus, | ||
| 161 | Qt::Key_Enter, | ||
| 162 | Qt::Key_1, | ||
| 163 | Qt::Key_2, | ||
| 164 | Qt::Key_3, | ||
| 165 | Qt::Key_4, | ||
| 166 | Qt::Key_5, | ||
| 167 | Qt::Key_6, | ||
| 168 | Qt::Key_7, | ||
| 169 | Qt::Key_8, | ||
| 170 | Qt::Key_9, | ||
| 171 | Qt::Key_0, | ||
| 172 | Qt::Key_Period, | ||
| 173 | |||
| 174 | 0, | ||
| 175 | 0, | ||
| 176 | Qt::Key_PowerOff, | ||
| 177 | Qt::Key_Equal, | ||
| 178 | |||
| 179 | Qt::Key_F13, | ||
| 180 | Qt::Key_F14, | ||
| 181 | Qt::Key_F15, | ||
| 182 | Qt::Key_F16, | ||
| 183 | Qt::Key_F17, | ||
| 184 | Qt::Key_F18, | ||
| 185 | Qt::Key_F19, | ||
| 186 | Qt::Key_F20, | ||
| 187 | Qt::Key_F21, | ||
| 188 | Qt::Key_F22, | ||
| 189 | Qt::Key_F23, | ||
| 190 | Qt::Key_F24, | ||
| 191 | |||
| 192 | Qt::Key_Open, | ||
| 193 | Qt::Key_Help, | ||
| 194 | Qt::Key_Menu, | ||
| 195 | 0, | ||
| 196 | Qt::Key_Stop, | ||
| 197 | Qt::Key_AudioRepeat, | ||
| 198 | Qt::Key_Undo, | ||
| 199 | Qt::Key_Cut, | ||
| 200 | Qt::Key_Copy, | ||
| 201 | Qt::Key_Paste, | ||
| 202 | Qt::Key_Find, | ||
| 203 | Qt::Key_VolumeMute, | ||
| 204 | Qt::Key_VolumeUp, | ||
| 205 | Qt::Key_VolumeDown, | ||
| 206 | Qt::Key_CapsLock, | ||
| 207 | Qt::Key_NumLock, | ||
| 208 | Qt::Key_ScrollLock, | ||
| 209 | Qt::Key_Comma, | ||
| 210 | |||
| 211 | Qt::Key_ParenLeft, | ||
| 212 | Qt::Key_ParenRight, | ||
| 213 | }; | ||
| 214 | |||
| 215 | const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = { | ||
| 216 | Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft, | ||
| 217 | Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight, | ||
| 218 | }; | ||
| 219 | |||
| 220 | // This shouldn't have anything except static initializers (no functions). So | 63 | // This shouldn't have anything except static initializers (no functions). So |
| 221 | // QKeySequence(...).toString() is NOT ALLOWED HERE. | 64 | // QKeySequence(...).toString() is NOT ALLOWED HERE. |
| 222 | // This must be in alphabetical order according to action name as it must have the same order as | 65 | // This must be in alphabetical order according to action name as it must have the same order as |
| @@ -430,18 +273,6 @@ void Config::ReadPlayerValue(std::size_t player_index) { | |||
| 430 | } | 273 | } |
| 431 | } | 274 | } |
| 432 | 275 | ||
| 433 | for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { | ||
| 434 | auto& player_vibrations = player.vibrations[i]; | ||
| 435 | |||
| 436 | player_vibrations = | ||
| 437 | qt_config | ||
| 438 | ->value(QStringLiteral("%1").arg(player_prefix) + | ||
| 439 | QString::fromUtf8(Settings::NativeVibration::mapping[i]), | ||
| 440 | QString{}) | ||
| 441 | .toString() | ||
| 442 | .toStdString(); | ||
| 443 | } | ||
| 444 | |||
| 445 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | 276 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { |
| 446 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | 277 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); |
| 447 | auto& player_motions = player.motions[i]; | 278 | auto& player_motions = player.motions[i]; |
| @@ -496,35 +327,10 @@ void Config::ReadDebugValues() { | |||
| 496 | 327 | ||
| 497 | void Config::ReadKeyboardValues() { | 328 | void Config::ReadKeyboardValues() { |
| 498 | ReadBasicSetting(Settings::values.keyboard_enabled); | 329 | ReadBasicSetting(Settings::values.keyboard_enabled); |
| 499 | |||
| 500 | std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), | ||
| 501 | Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); | ||
| 502 | std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(), | ||
| 503 | Settings::values.keyboard_keys.begin() + | ||
| 504 | Settings::NativeKeyboard::LeftControlKey, | ||
| 505 | InputCommon::GenerateKeyboardParam); | ||
| 506 | std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(), | ||
| 507 | Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam); | ||
| 508 | } | 330 | } |
| 509 | 331 | ||
| 510 | void Config::ReadMouseValues() { | 332 | void Config::ReadMouseValues() { |
| 511 | ReadBasicSetting(Settings::values.mouse_enabled); | 333 | ReadBasicSetting(Settings::values.mouse_enabled); |
| 512 | |||
| 513 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 514 | const std::string default_param = | ||
| 515 | InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); | ||
| 516 | auto& mouse_buttons = Settings::values.mouse_buttons[i]; | ||
| 517 | |||
| 518 | mouse_buttons = qt_config | ||
| 519 | ->value(QStringLiteral("mouse_") + | ||
| 520 | QString::fromUtf8(Settings::NativeMouseButton::mapping[i]), | ||
| 521 | QString::fromStdString(default_param)) | ||
| 522 | .toString() | ||
| 523 | .toStdString(); | ||
| 524 | if (mouse_buttons.empty()) { | ||
| 525 | mouse_buttons = default_param; | ||
| 526 | } | ||
| 527 | } | ||
| 528 | } | 334 | } |
| 529 | 335 | ||
| 530 | void Config::ReadTouchscreenValues() { | 336 | void Config::ReadTouchscreenValues() { |
| @@ -574,7 +380,6 @@ void Config::ReadControlValues() { | |||
| 574 | 380 | ||
| 575 | ReadBasicSetting(Settings::values.tas_enable); | 381 | ReadBasicSetting(Settings::values.tas_enable); |
| 576 | ReadBasicSetting(Settings::values.tas_loop); | 382 | ReadBasicSetting(Settings::values.tas_loop); |
| 577 | ReadBasicSetting(Settings::values.tas_swap_controllers); | ||
| 578 | ReadBasicSetting(Settings::values.pause_tas_on_load); | 383 | ReadBasicSetting(Settings::values.pause_tas_on_load); |
| 579 | 384 | ||
| 580 | ReadGlobalSetting(Settings::values.use_docked_mode); | 385 | ReadGlobalSetting(Settings::values.use_docked_mode); |
| @@ -625,13 +430,12 @@ void Config::ReadMotionTouchValues() { | |||
| 625 | } | 430 | } |
| 626 | qt_config->endArray(); | 431 | qt_config->endArray(); |
| 627 | 432 | ||
| 628 | ReadBasicSetting(Settings::values.motion_device); | ||
| 629 | ReadBasicSetting(Settings::values.touch_device); | 433 | ReadBasicSetting(Settings::values.touch_device); |
| 630 | ReadBasicSetting(Settings::values.use_touch_from_button); | ||
| 631 | ReadBasicSetting(Settings::values.touch_from_button_map_index); | 434 | ReadBasicSetting(Settings::values.touch_from_button_map_index); |
| 632 | Settings::values.touch_from_button_map_index = std::clamp( | 435 | Settings::values.touch_from_button_map_index = std::clamp( |
| 633 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); | 436 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); |
| 634 | ReadBasicSetting(Settings::values.udp_input_servers); | 437 | ReadBasicSetting(Settings::values.udp_input_servers); |
| 438 | ReadBasicSetting(Settings::values.enable_udp_controller); | ||
| 635 | } | 439 | } |
| 636 | 440 | ||
| 637 | void Config::ReadCoreValues() { | 441 | void Config::ReadCoreValues() { |
| @@ -704,6 +508,7 @@ void Config::ReadDebuggingValues() { | |||
| 704 | ReadBasicSetting(Settings::values.extended_logging); | 508 | ReadBasicSetting(Settings::values.extended_logging); |
| 705 | ReadBasicSetting(Settings::values.use_debug_asserts); | 509 | ReadBasicSetting(Settings::values.use_debug_asserts); |
| 706 | ReadBasicSetting(Settings::values.use_auto_stub); | 510 | ReadBasicSetting(Settings::values.use_auto_stub); |
| 511 | ReadBasicSetting(Settings::values.enable_all_controllers); | ||
| 707 | 512 | ||
| 708 | qt_config->endGroup(); | 513 | qt_config->endGroup(); |
| 709 | } | 514 | } |
| @@ -830,6 +635,7 @@ void Config::ReadRendererValues() { | |||
| 830 | ReadGlobalSetting(Settings::values.max_anisotropy); | 635 | ReadGlobalSetting(Settings::values.max_anisotropy); |
| 831 | ReadGlobalSetting(Settings::values.use_speed_limit); | 636 | ReadGlobalSetting(Settings::values.use_speed_limit); |
| 832 | ReadGlobalSetting(Settings::values.speed_limit); | 637 | ReadGlobalSetting(Settings::values.speed_limit); |
| 638 | ReadGlobalSetting(Settings::values.fps_cap); | ||
| 833 | ReadGlobalSetting(Settings::values.use_disk_shader_cache); | 639 | ReadGlobalSetting(Settings::values.use_disk_shader_cache); |
| 834 | ReadGlobalSetting(Settings::values.gpu_accuracy); | 640 | ReadGlobalSetting(Settings::values.gpu_accuracy); |
| 835 | ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); | 641 | ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); |
| @@ -844,7 +650,6 @@ void Config::ReadRendererValues() { | |||
| 844 | ReadGlobalSetting(Settings::values.bg_blue); | 650 | ReadGlobalSetting(Settings::values.bg_blue); |
| 845 | 651 | ||
| 846 | if (global) { | 652 | if (global) { |
| 847 | ReadBasicSetting(Settings::values.fps_cap); | ||
| 848 | ReadBasicSetting(Settings::values.renderer_debug); | 653 | ReadBasicSetting(Settings::values.renderer_debug); |
| 849 | ReadBasicSetting(Settings::values.renderer_shader_feedback); | 654 | ReadBasicSetting(Settings::values.renderer_shader_feedback); |
| 850 | ReadBasicSetting(Settings::values.enable_nsight_aftermath); | 655 | ReadBasicSetting(Settings::values.enable_nsight_aftermath); |
| @@ -971,6 +776,7 @@ void Config::ReadUIGamelistValues() { | |||
| 971 | ReadBasicSetting(UISettings::values.row_1_text_id); | 776 | ReadBasicSetting(UISettings::values.row_1_text_id); |
| 972 | ReadBasicSetting(UISettings::values.row_2_text_id); | 777 | ReadBasicSetting(UISettings::values.row_2_text_id); |
| 973 | ReadBasicSetting(UISettings::values.cache_game_list); | 778 | ReadBasicSetting(UISettings::values.cache_game_list); |
| 779 | ReadBasicSetting(UISettings::values.favorites_expanded); | ||
| 974 | const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); | 780 | const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); |
| 975 | for (int i = 0; i < favorites_size; i++) { | 781 | for (int i = 0; i < favorites_size; i++) { |
| 976 | qt_config->setArrayIndex(i); | 782 | qt_config->setArrayIndex(i); |
| @@ -1075,11 +881,6 @@ void Config::SavePlayerValue(std::size_t player_index) { | |||
| 1075 | QString::fromStdString(player.analogs[i]), | 881 | QString::fromStdString(player.analogs[i]), |
| 1076 | QString::fromStdString(default_param)); | 882 | QString::fromStdString(default_param)); |
| 1077 | } | 883 | } |
| 1078 | for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { | ||
| 1079 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + | ||
| 1080 | QString::fromStdString(Settings::NativeVibration::mapping[i]), | ||
| 1081 | QString::fromStdString(player.vibrations[i]), QString{}); | ||
| 1082 | } | ||
| 1083 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | 884 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { |
| 1084 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | 885 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); |
| 1085 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + | 886 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + |
| @@ -1111,15 +912,6 @@ void Config::SaveDebugValues() { | |||
| 1111 | 912 | ||
| 1112 | void Config::SaveMouseValues() { | 913 | void Config::SaveMouseValues() { |
| 1113 | WriteBasicSetting(Settings::values.mouse_enabled); | 914 | WriteBasicSetting(Settings::values.mouse_enabled); |
| 1114 | |||
| 1115 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 1116 | const std::string default_param = | ||
| 1117 | InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); | ||
| 1118 | WriteSetting(QStringLiteral("mouse_") + | ||
| 1119 | QString::fromStdString(Settings::NativeMouseButton::mapping[i]), | ||
| 1120 | QString::fromStdString(Settings::values.mouse_buttons[i]), | ||
| 1121 | QString::fromStdString(default_param)); | ||
| 1122 | } | ||
| 1123 | } | 915 | } |
| 1124 | 916 | ||
| 1125 | void Config::SaveTouchscreenValues() { | 917 | void Config::SaveTouchscreenValues() { |
| @@ -1133,11 +925,10 @@ void Config::SaveTouchscreenValues() { | |||
| 1133 | } | 925 | } |
| 1134 | 926 | ||
| 1135 | void Config::SaveMotionTouchValues() { | 927 | void Config::SaveMotionTouchValues() { |
| 1136 | WriteBasicSetting(Settings::values.motion_device); | ||
| 1137 | WriteBasicSetting(Settings::values.touch_device); | 928 | WriteBasicSetting(Settings::values.touch_device); |
| 1138 | WriteBasicSetting(Settings::values.use_touch_from_button); | ||
| 1139 | WriteBasicSetting(Settings::values.touch_from_button_map_index); | 929 | WriteBasicSetting(Settings::values.touch_from_button_map_index); |
| 1140 | WriteBasicSetting(Settings::values.udp_input_servers); | 930 | WriteBasicSetting(Settings::values.udp_input_servers); |
| 931 | WriteBasicSetting(Settings::values.enable_udp_controller); | ||
| 1141 | 932 | ||
| 1142 | qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); | 933 | qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); |
| 1143 | for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { | 934 | for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { |
| @@ -1210,7 +1001,6 @@ void Config::SaveControlValues() { | |||
| 1210 | 1001 | ||
| 1211 | WriteBasicSetting(Settings::values.tas_enable); | 1002 | WriteBasicSetting(Settings::values.tas_enable); |
| 1212 | WriteBasicSetting(Settings::values.tas_loop); | 1003 | WriteBasicSetting(Settings::values.tas_loop); |
| 1213 | WriteBasicSetting(Settings::values.tas_swap_controllers); | ||
| 1214 | WriteBasicSetting(Settings::values.pause_tas_on_load); | 1004 | WriteBasicSetting(Settings::values.pause_tas_on_load); |
| 1215 | 1005 | ||
| 1216 | qt_config->endGroup(); | 1006 | qt_config->endGroup(); |
| @@ -1263,6 +1053,7 @@ void Config::SaveDebuggingValues() { | |||
| 1263 | WriteBasicSetting(Settings::values.quest_flag); | 1053 | WriteBasicSetting(Settings::values.quest_flag); |
| 1264 | WriteBasicSetting(Settings::values.use_debug_asserts); | 1054 | WriteBasicSetting(Settings::values.use_debug_asserts); |
| 1265 | WriteBasicSetting(Settings::values.disable_macro_jit); | 1055 | WriteBasicSetting(Settings::values.disable_macro_jit); |
| 1056 | WriteBasicSetting(Settings::values.enable_all_controllers); | ||
| 1266 | 1057 | ||
| 1267 | qt_config->endGroup(); | 1058 | qt_config->endGroup(); |
| 1268 | } | 1059 | } |
| @@ -1382,6 +1173,7 @@ void Config::SaveRendererValues() { | |||
| 1382 | WriteGlobalSetting(Settings::values.max_anisotropy); | 1173 | WriteGlobalSetting(Settings::values.max_anisotropy); |
| 1383 | WriteGlobalSetting(Settings::values.use_speed_limit); | 1174 | WriteGlobalSetting(Settings::values.use_speed_limit); |
| 1384 | WriteGlobalSetting(Settings::values.speed_limit); | 1175 | WriteGlobalSetting(Settings::values.speed_limit); |
| 1176 | WriteGlobalSetting(Settings::values.fps_cap); | ||
| 1385 | WriteGlobalSetting(Settings::values.use_disk_shader_cache); | 1177 | WriteGlobalSetting(Settings::values.use_disk_shader_cache); |
| 1386 | WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), | 1178 | WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), |
| 1387 | static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)), | 1179 | static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)), |
| @@ -1405,7 +1197,6 @@ void Config::SaveRendererValues() { | |||
| 1405 | WriteGlobalSetting(Settings::values.bg_blue); | 1197 | WriteGlobalSetting(Settings::values.bg_blue); |
| 1406 | 1198 | ||
| 1407 | if (global) { | 1199 | if (global) { |
| 1408 | WriteBasicSetting(Settings::values.fps_cap); | ||
| 1409 | WriteBasicSetting(Settings::values.renderer_debug); | 1200 | WriteBasicSetting(Settings::values.renderer_debug); |
| 1410 | WriteBasicSetting(Settings::values.renderer_shader_feedback); | 1201 | WriteBasicSetting(Settings::values.renderer_shader_feedback); |
| 1411 | WriteBasicSetting(Settings::values.enable_nsight_aftermath); | 1202 | WriteBasicSetting(Settings::values.enable_nsight_aftermath); |
| @@ -1510,6 +1301,7 @@ void Config::SaveUIGamelistValues() { | |||
| 1510 | WriteBasicSetting(UISettings::values.row_1_text_id); | 1301 | WriteBasicSetting(UISettings::values.row_1_text_id); |
| 1511 | WriteBasicSetting(UISettings::values.row_2_text_id); | 1302 | WriteBasicSetting(UISettings::values.row_2_text_id); |
| 1512 | WriteBasicSetting(UISettings::values.cache_game_list); | 1303 | WriteBasicSetting(UISettings::values.cache_game_list); |
| 1304 | WriteBasicSetting(UISettings::values.favorites_expanded); | ||
| 1513 | qt_config->beginWriteArray(QStringLiteral("favorites")); | 1305 | qt_config->beginWriteArray(QStringLiteral("favorites")); |
| 1514 | for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { | 1306 | for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { |
| 1515 | qt_config->setArrayIndex(i); | 1307 | qt_config->setArrayIndex(i); |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 07bfa0360..633fc295b 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -42,6 +42,7 @@ void ConfigureDebug::SetConfiguration() { | |||
| 42 | ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue()); | 42 | ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue()); |
| 43 | ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue()); | 43 | ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue()); |
| 44 | ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); | 44 | ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); |
| 45 | ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); | ||
| 45 | ui->enable_graphics_debugging->setEnabled(runtime_lock); | 46 | ui->enable_graphics_debugging->setEnabled(runtime_lock); |
| 46 | ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); | 47 | ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); |
| 47 | ui->enable_shader_feedback->setEnabled(runtime_lock); | 48 | ui->enable_shader_feedback->setEnabled(runtime_lock); |
| @@ -67,6 +68,7 @@ void ConfigureDebug::ApplyConfiguration() { | |||
| 67 | Settings::values.quest_flag = ui->quest_flag->isChecked(); | 68 | Settings::values.quest_flag = ui->quest_flag->isChecked(); |
| 68 | Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); | 69 | Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); |
| 69 | Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); | 70 | Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); |
| 71 | Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); | ||
| 70 | Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); | 72 | Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); |
| 71 | Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); | 73 | Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); |
| 72 | Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); | 74 | Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index b884a56b0..0f3b51c8d 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -198,6 +198,13 @@ | |||
| 198 | </property> | 198 | </property> |
| 199 | </widget> | 199 | </widget> |
| 200 | </item> | 200 | </item> |
| 201 | <item row="1" column="1"> | ||
| 202 | <widget class="QCheckBox" name="enable_all_controllers"> | ||
| 203 | <property name="text"> | ||
| 204 | <string>Enable all Controller Types</string> | ||
| 205 | </property> | ||
| 206 | </widget> | ||
| 207 | </item> | ||
| 201 | </layout> | 208 | </layout> |
| 202 | </widget> | 209 | </widget> |
| 203 | </item> | 210 | </item> |
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp index 31ec48384..9a8de92a1 100644 --- a/src/yuzu/configuration/configure_debug_controller.cpp +++ b/src/yuzu/configuration/configure_debug_controller.cpp | |||
| @@ -2,17 +2,18 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core.h" | 5 | #include "core/hid/hid_core.h" |
| 6 | #include "ui_configure_debug_controller.h" | 6 | #include "ui_configure_debug_controller.h" |
| 7 | #include "yuzu/configuration/configure_debug_controller.h" | 7 | #include "yuzu/configuration/configure_debug_controller.h" |
| 8 | #include "yuzu/configuration/configure_input_player.h" | 8 | #include "yuzu/configuration/configure_input_player.h" |
| 9 | 9 | ||
| 10 | ConfigureDebugController::ConfigureDebugController(QWidget* parent, | 10 | ConfigureDebugController::ConfigureDebugController(QWidget* parent, |
| 11 | InputCommon::InputSubsystem* input_subsystem, | 11 | InputCommon::InputSubsystem* input_subsystem, |
| 12 | InputProfiles* profiles, Core::System& system) | 12 | InputProfiles* profiles, |
| 13 | Core::HID::HIDCore& hid_core, bool is_powered_on) | ||
| 13 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()), | 14 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()), |
| 14 | debug_controller( | 15 | debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, |
| 15 | new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, system, true)) { | 16 | hid_core, is_powered_on, true)) { |
| 16 | ui->setupUi(this); | 17 | ui->setupUi(this); |
| 17 | 18 | ||
| 18 | ui->controllerLayout->addWidget(debug_controller); | 19 | ui->controllerLayout->addWidget(debug_controller); |
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h index 6e17c5aa0..d716edbc2 100644 --- a/src/yuzu/configuration/configure_debug_controller.h +++ b/src/yuzu/configuration/configure_debug_controller.h | |||
| @@ -13,8 +13,8 @@ class ConfigureInputPlayer; | |||
| 13 | 13 | ||
| 14 | class InputProfiles; | 14 | class InputProfiles; |
| 15 | 15 | ||
| 16 | namespace Core { | 16 | namespace Core::HID { |
| 17 | class System; | 17 | class HIDCore; |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | namespace InputCommon { | 20 | namespace InputCommon { |
| @@ -30,7 +30,8 @@ class ConfigureDebugController : public QDialog { | |||
| 30 | 30 | ||
| 31 | public: | 31 | public: |
| 32 | explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem, | 32 | explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem, |
| 33 | InputProfiles* profiles, Core::System& system); | 33 | InputProfiles* profiles, Core::HID::HIDCore& hid_core, |
| 34 | bool is_powered_on); | ||
| 34 | ~ConfigureDebugController() override; | 35 | ~ConfigureDebugController() override; |
| 35 | 36 | ||
| 36 | void ApplyConfiguration(); | 37 | void ApplyConfiguration(); |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 7af3ea97e..566879317 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -30,6 +30,9 @@ ConfigureGeneral::ConfigureGeneral(const Core::System& system_, QWidget* parent) | |||
| 30 | 30 | ||
| 31 | connect(ui->button_reset_defaults, &QPushButton::clicked, this, | 31 | connect(ui->button_reset_defaults, &QPushButton::clicked, this, |
| 32 | &ConfigureGeneral::ResetDefaults); | 32 | &ConfigureGeneral::ResetDefaults); |
| 33 | |||
| 34 | ui->fps_cap_label->setVisible(Settings::IsConfiguringGlobal()); | ||
| 35 | ui->fps_cap_combobox->setVisible(!Settings::IsConfiguringGlobal()); | ||
| 33 | } | 36 | } |
| 34 | 37 | ||
| 35 | ConfigureGeneral::~ConfigureGeneral() = default; | 38 | ConfigureGeneral::~ConfigureGeneral() = default; |
| @@ -57,6 +60,11 @@ void ConfigureGeneral::SetConfiguration() { | |||
| 57 | } else { | 60 | } else { |
| 58 | ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() && | 61 | ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() && |
| 59 | use_speed_limit != ConfigurationShared::CheckState::Global); | 62 | use_speed_limit != ConfigurationShared::CheckState::Global); |
| 63 | |||
| 64 | ui->fps_cap_combobox->setCurrentIndex(Settings::values.fps_cap.UsingGlobal() ? 0 : 1); | ||
| 65 | ui->fps_cap->setEnabled(!Settings::values.fps_cap.UsingGlobal()); | ||
| 66 | ConfigurationShared::SetHighlight(ui->fps_cap_layout, | ||
| 67 | !Settings::values.fps_cap.UsingGlobal()); | ||
| 60 | } | 68 | } |
| 61 | } | 69 | } |
| 62 | 70 | ||
| @@ -106,6 +114,13 @@ void ConfigureGeneral::ApplyConfiguration() { | |||
| 106 | Qt::Checked); | 114 | Qt::Checked); |
| 107 | Settings::values.speed_limit.SetValue(ui->speed_limit->value()); | 115 | Settings::values.speed_limit.SetValue(ui->speed_limit->value()); |
| 108 | } | 116 | } |
| 117 | |||
| 118 | if (ui->fps_cap_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 119 | Settings::values.fps_cap.SetGlobal(true); | ||
| 120 | } else { | ||
| 121 | Settings::values.fps_cap.SetGlobal(false); | ||
| 122 | Settings::values.fps_cap.SetValue(ui->fps_cap->value()); | ||
| 123 | } | ||
| 109 | } | 124 | } |
| 110 | } | 125 | } |
| 111 | 126 | ||
| @@ -148,4 +163,9 @@ void ConfigureGeneral::SetupPerGameUI() { | |||
| 148 | ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && | 163 | ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && |
| 149 | (use_speed_limit != ConfigurationShared::CheckState::Global)); | 164 | (use_speed_limit != ConfigurationShared::CheckState::Global)); |
| 150 | }); | 165 | }); |
| 166 | |||
| 167 | connect(ui->fps_cap_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) { | ||
| 168 | ui->fps_cap->setEnabled(index == 1); | ||
| 169 | ConfigurationShared::SetHighlight(ui->fps_cap_layout, index == 1); | ||
| 170 | }); | ||
| 151 | } | 171 | } |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index f9f0e3ebf..112dc72b3 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | <rect> | 6 | <rect> |
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>329</width> | 9 | <width>744</width> |
| 10 | <height>407</height> | 10 | <height>568</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -28,34 +28,85 @@ | |||
| 28 | <item> | 28 | <item> |
| 29 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | 29 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> |
| 30 | <item> | 30 | <item> |
| 31 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | 31 | <widget class="QWidget" name="fps_cap_layout" native="true"> |
| 32 | <item> | 32 | <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1"> |
| 33 | <widget class="QLabel" name="fps_cap_label"> | 33 | <property name="leftMargin"> |
| 34 | <number>0</number> | ||
| 35 | </property> | ||
| 36 | <property name="topMargin"> | ||
| 37 | <number>0</number> | ||
| 38 | </property> | ||
| 39 | <property name="rightMargin"> | ||
| 40 | <number>0</number> | ||
| 41 | </property> | ||
| 42 | <property name="bottomMargin"> | ||
| 43 | <number>0</number> | ||
| 44 | </property> | ||
| 45 | <item> | ||
| 46 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 47 | <item> | ||
| 48 | <widget class="QComboBox" name="fps_cap_combobox"> | ||
| 49 | <property name="currentText"> | ||
| 50 | <string>Use global framerate cap</string> | ||
| 51 | </property> | ||
| 52 | <property name="currentIndex"> | ||
| 53 | <number>0</number> | ||
| 54 | </property> | ||
| 55 | <item> | ||
| 34 | <property name="text"> | 56 | <property name="text"> |
| 35 | <string>Framerate Cap</string> | 57 | <string>Use global framerate cap</string> |
| 36 | </property> | 58 | </property> |
| 37 | <property name="toolTip"> | 59 | </item> |
| 38 | <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string> | 60 | <item> |
| 61 | <property name="text"> | ||
| 62 | <string>Set framerate cap:</string> | ||
| 39 | </property> | 63 | </property> |
| 64 | </item> | ||
| 40 | </widget> | 65 | </widget> |
| 41 | </item> | 66 | </item> |
| 42 | <item> | 67 | <item> |
| 43 | <widget class="QSpinBox" name="fps_cap"> | 68 | <widget class="QLabel" name="fps_cap_label"> |
| 44 | <property name="suffix"> | 69 | <property name="toolTip"> |
| 45 | <string>x</string> | 70 | <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string> |
| 46 | </property> | 71 | </property> |
| 47 | <property name="minimum"> | 72 | <property name="text"> |
| 48 | <number>1</number> | 73 | <string>Framerate Cap</string> |
| 49 | </property> | 74 | </property> |
| 50 | <property name="maximum"> | ||
| 51 | <number>1000</number> | ||
| 52 | </property> | ||
| 53 | <property name="value"> | ||
| 54 | <number>500</number> | ||
| 55 | </property> | ||
| 56 | </widget> | 75 | </widget> |
| 57 | </item> | 76 | </item> |
| 77 | <item> | ||
| 78 | <spacer name="horizontalSpacer"> | ||
| 79 | <property name="orientation"> | ||
| 80 | <enum>Qt::Horizontal</enum> | ||
| 81 | </property> | ||
| 82 | <property name="sizeHint" stdset="0"> | ||
| 83 | <size> | ||
| 84 | <width>40</width> | ||
| 85 | <height>20</height> | ||
| 86 | </size> | ||
| 87 | </property> | ||
| 88 | </spacer> | ||
| 89 | </item> | ||
| 90 | </layout> | ||
| 91 | </item> | ||
| 92 | <item> | ||
| 93 | <widget class="QSpinBox" name="fps_cap"> | ||
| 94 | <property name="suffix"> | ||
| 95 | <string>x</string> | ||
| 96 | </property> | ||
| 97 | <property name="minimum"> | ||
| 98 | <number>1</number> | ||
| 99 | </property> | ||
| 100 | <property name="maximum"> | ||
| 101 | <number>1000</number> | ||
| 102 | </property> | ||
| 103 | <property name="value"> | ||
| 104 | <number>500</number> | ||
| 105 | </property> | ||
| 106 | </widget> | ||
| 107 | </item> | ||
| 58 | </layout> | 108 | </layout> |
| 109 | </widget> | ||
| 59 | </item> | 110 | </item> |
| 60 | <item> | 111 | <item> |
| 61 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | 112 | <layout class="QHBoxLayout" name="horizontalLayout_2"> |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 660b68c1c..9241678e4 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -429,7 +429,7 @@ | |||
| 429 | </item> | 429 | </item> |
| 430 | <item> | 430 | <item> |
| 431 | <property name="text"> | 431 | <property name="text"> |
| 432 | <string>AMD's FidelityFX™️ Super Resolution [Vulkan Only]</string> | 432 | <string>AMD FidelityFX™️ Super Resolution [Vulkan Only]</string> |
| 433 | </property> | 433 | </property> |
| 434 | </item> | 434 | </item> |
| 435 | </widget> | 435 | </widget> |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 1599299db..d53179dbb 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | #include <QTimer> | 10 | #include <QTimer> |
| 11 | 11 | ||
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/hid/emulated_controller.h" | ||
| 14 | #include "core/hid/hid_core.h" | ||
| 13 | #include "core/hle/service/am/am.h" | 15 | #include "core/hle/service/am/am.h" |
| 14 | #include "core/hle/service/am/applet_ae.h" | 16 | #include "core/hle/service/am/applet_ae.h" |
| 15 | #include "core/hle/service/am/applet_oe.h" | 17 | #include "core/hle/service/am/applet_oe.h" |
| @@ -22,7 +24,6 @@ | |||
| 22 | #include "yuzu/configuration/configure_input_advanced.h" | 24 | #include "yuzu/configuration/configure_input_advanced.h" |
| 23 | #include "yuzu/configuration/configure_input_player.h" | 25 | #include "yuzu/configuration/configure_input_player.h" |
| 24 | #include "yuzu/configuration/configure_motion_touch.h" | 26 | #include "yuzu/configuration/configure_motion_touch.h" |
| 25 | #include "yuzu/configuration/configure_mouse_advanced.h" | ||
| 26 | #include "yuzu/configuration/configure_touchscreen_advanced.h" | 27 | #include "yuzu/configuration/configure_touchscreen_advanced.h" |
| 27 | #include "yuzu/configuration/configure_vibration.h" | 28 | #include "yuzu/configuration/configure_vibration.h" |
| 28 | #include "yuzu/configuration/input_profiles.h" | 29 | #include "yuzu/configuration/input_profiles.h" |
| @@ -75,23 +76,25 @@ ConfigureInput::~ConfigureInput() = default; | |||
| 75 | 76 | ||
| 76 | void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | 77 | void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, |
| 77 | std::size_t max_players) { | 78 | std::size_t max_players) { |
| 79 | const bool is_powered_on = system.IsPoweredOn(); | ||
| 80 | auto& hid_core = system.HIDCore(); | ||
| 78 | player_controllers = { | 81 | player_controllers = { |
| 79 | new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(), | 82 | new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(), |
| 80 | system), | 83 | hid_core, is_powered_on), |
| 81 | new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(), | 84 | new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(), |
| 82 | system), | 85 | hid_core, is_powered_on), |
| 83 | new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(), | 86 | new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(), |
| 84 | system), | 87 | hid_core, is_powered_on), |
| 85 | new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(), | 88 | new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(), |
| 86 | system), | 89 | hid_core, is_powered_on), |
| 87 | new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(), | 90 | new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(), |
| 88 | system), | 91 | hid_core, is_powered_on), |
| 89 | new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(), | 92 | new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(), |
| 90 | system), | 93 | hid_core, is_powered_on), |
| 91 | new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(), | 94 | new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(), |
| 92 | system), | 95 | hid_core, is_powered_on), |
| 93 | new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(), | 96 | new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(), |
| 94 | system), | 97 | hid_core, is_powered_on), |
| 95 | }; | 98 | }; |
| 96 | 99 | ||
| 97 | player_tabs = { | 100 | player_tabs = { |
| @@ -114,6 +117,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | |||
| 114 | player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); | 117 | player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); |
| 115 | player_tabs[i]->layout()->addWidget(player_controllers[i]); | 118 | player_tabs[i]->layout()->addWidget(player_controllers[i]); |
| 116 | connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) { | 119 | connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) { |
| 120 | // Ensures that the controllers are always connected in sequential order | ||
| 117 | if (is_connected) { | 121 | if (is_connected) { |
| 118 | for (std::size_t index = 0; index <= i; ++index) { | 122 | for (std::size_t index = 0; index <= i; ++index) { |
| 119 | player_connected[index]->setChecked(is_connected); | 123 | player_connected[index]->setChecked(is_connected); |
| @@ -146,13 +150,12 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | |||
| 146 | advanced = new ConfigureInputAdvanced(this); | 150 | advanced = new ConfigureInputAdvanced(this); |
| 147 | ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); | 151 | ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); |
| 148 | ui->tabAdvanced->layout()->addWidget(advanced); | 152 | ui->tabAdvanced->layout()->addWidget(advanced); |
| 149 | connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] { | 153 | |
| 150 | CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get(), | 154 | connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, |
| 151 | system); | 155 | [this, input_subsystem, &hid_core, is_powered_on] { |
| 152 | }); | 156 | CallConfigureDialog<ConfigureDebugController>( |
| 153 | connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] { | 157 | *this, input_subsystem, profiles.get(), hid_core, is_powered_on); |
| 154 | CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem); | 158 | }); |
| 155 | }); | ||
| 156 | connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog, | 159 | connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog, |
| 157 | [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); | 160 | [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); |
| 158 | connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog, | 161 | connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog, |
| @@ -184,22 +187,8 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const { | |||
| 184 | void ConfigureInput::ApplyConfiguration() { | 187 | void ConfigureInput::ApplyConfiguration() { |
| 185 | for (auto* controller : player_controllers) { | 188 | for (auto* controller : player_controllers) { |
| 186 | controller->ApplyConfiguration(); | 189 | controller->ApplyConfiguration(); |
| 187 | controller->TryDisconnectSelectedController(); | ||
| 188 | } | ||
| 189 | |||
| 190 | // This emulates a delay between disconnecting and reconnecting controllers as some games | ||
| 191 | // do not respond to a change in controller type if it was instantaneous. | ||
| 192 | using namespace std::chrono_literals; | ||
| 193 | std::this_thread::sleep_for(150ms); | ||
| 194 | |||
| 195 | for (auto* controller : player_controllers) { | ||
| 196 | controller->TryConnectSelectedController(); | ||
| 197 | } | 190 | } |
| 198 | 191 | ||
| 199 | // This emulates a delay between disconnecting and reconnecting controllers as some games | ||
| 200 | // do not respond to a change in controller type if it was instantaneous. | ||
| 201 | std::this_thread::sleep_for(150ms); | ||
| 202 | |||
| 203 | advanced->ApplyConfiguration(); | 192 | advanced->ApplyConfiguration(); |
| 204 | 193 | ||
| 205 | const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); | 194 | const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); |
| @@ -223,8 +212,10 @@ void ConfigureInput::RetranslateUI() { | |||
| 223 | } | 212 | } |
| 224 | 213 | ||
| 225 | void ConfigureInput::LoadConfiguration() { | 214 | void ConfigureInput::LoadConfiguration() { |
| 215 | const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 216 | |||
| 226 | LoadPlayerControllerIndices(); | 217 | LoadPlayerControllerIndices(); |
| 227 | UpdateDockedState(Settings::values.players.GetValue()[8].connected); | 218 | UpdateDockedState(handheld->IsConnected()); |
| 228 | 219 | ||
| 229 | ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); | 220 | ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); |
| 230 | ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); | 221 | ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); |
| @@ -232,9 +223,16 @@ void ConfigureInput::LoadConfiguration() { | |||
| 232 | 223 | ||
| 233 | void ConfigureInput::LoadPlayerControllerIndices() { | 224 | void ConfigureInput::LoadPlayerControllerIndices() { |
| 234 | for (std::size_t i = 0; i < player_connected.size(); ++i) { | 225 | for (std::size_t i = 0; i < player_connected.size(); ++i) { |
| 235 | const auto connected = Settings::values.players.GetValue()[i].connected || | 226 | if (i == 0) { |
| 236 | (i == 0 && Settings::values.players.GetValue()[8].connected); | 227 | auto* handheld = |
| 237 | player_connected[i]->setChecked(connected); | 228 | system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 229 | if (handheld->IsConnected()) { | ||
| 230 | player_connected[i]->setChecked(true); | ||
| 231 | continue; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(i); | ||
| 235 | player_connected[i]->setChecked(controller->IsConnected()); | ||
| 238 | } | 236 | } |
| 239 | } | 237 | } |
| 240 | 238 | ||
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index b30f09013..65c8e59ac 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp | |||
| @@ -82,7 +82,6 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) | |||
| 82 | 82 | ||
| 83 | connect(ui->debug_configure, &QPushButton::clicked, this, | 83 | connect(ui->debug_configure, &QPushButton::clicked, this, |
| 84 | [this] { CallDebugControllerDialog(); }); | 84 | [this] { CallDebugControllerDialog(); }); |
| 85 | connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); }); | ||
| 86 | connect(ui->touchscreen_advanced, &QPushButton::clicked, this, | 85 | connect(ui->touchscreen_advanced, &QPushButton::clicked, this, |
| 87 | [this] { CallTouchscreenConfigDialog(); }); | 86 | [this] { CallTouchscreenConfigDialog(); }); |
| 88 | connect(ui->buttonMotionTouch, &QPushButton::clicked, this, | 87 | connect(ui->buttonMotionTouch, &QPushButton::clicked, this, |
| @@ -131,6 +130,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() { | |||
| 131 | static_cast<float>(ui->mouse_panning_sensitivity->value()); | 130 | static_cast<float>(ui->mouse_panning_sensitivity->value()); |
| 132 | Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); | 131 | Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); |
| 133 | Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); | 132 | Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); |
| 133 | Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked(); | ||
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | void ConfigureInputAdvanced::LoadConfiguration() { | 136 | void ConfigureInputAdvanced::LoadConfiguration() { |
| @@ -161,6 +161,7 @@ void ConfigureInputAdvanced::LoadConfiguration() { | |||
| 161 | ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue()); | 161 | ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue()); |
| 162 | ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); | 162 | ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); |
| 163 | ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); | 163 | ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); |
| 164 | ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue()); | ||
| 164 | 165 | ||
| 165 | UpdateUIEnabled(); | 166 | UpdateUIEnabled(); |
| 166 | } | 167 | } |
| @@ -178,7 +179,8 @@ void ConfigureInputAdvanced::RetranslateUI() { | |||
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | void ConfigureInputAdvanced::UpdateUIEnabled() { | 181 | void ConfigureInputAdvanced::UpdateUIEnabled() { |
| 181 | ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked()); | ||
| 182 | ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); | 182 | ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); |
| 183 | ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); | 183 | ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); |
| 184 | ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked()); | ||
| 185 | ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked()); | ||
| 184 | } | 186 | } |
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index 9095206a0..df0e4d602 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui | |||
| @@ -2528,11 +2528,11 @@ | |||
| 2528 | <number>0</number> | 2528 | <number>0</number> |
| 2529 | </property> | 2529 | </property> |
| 2530 | <item> | 2530 | <item> |
| 2531 | <widget class="QGroupBox" name="gridGroupBox_3"> | 2531 | <widget class="QGroupBox" name="emulatedDevicesGroupBox"> |
| 2532 | <property name="title"> | 2532 | <property name="title"> |
| 2533 | <string>Other</string> | 2533 | <string>Emulated Devices</string> |
| 2534 | </property> | 2534 | </property> |
| 2535 | <layout class="QGridLayout" name="gridLayout_3"> | 2535 | <layout class="QGridLayout" name="emulatedDevicesGridLayout"> |
| 2536 | <item row="0" column="0"> | 2536 | <item row="0" column="0"> |
| 2537 | <widget class="QCheckBox" name="keyboard_enabled"> | 2537 | <widget class="QCheckBox" name="keyboard_enabled"> |
| 2538 | <property name="minimumSize"> | 2538 | <property name="minimumSize"> |
| @@ -2547,7 +2547,7 @@ | |||
| 2547 | </widget> | 2547 | </widget> |
| 2548 | </item> | 2548 | </item> |
| 2549 | <item row="1" column="0"> | 2549 | <item row="1" column="0"> |
| 2550 | <widget class="QCheckBox" name="emulate_analog_keyboard"> | 2550 | <widget class="QCheckBox" name="mouse_enabled"> |
| 2551 | <property name="minimumSize"> | 2551 | <property name="minimumSize"> |
| 2552 | <size> | 2552 | <size> |
| 2553 | <width>0</width> | 2553 | <width>0</width> |
| @@ -2555,53 +2555,18 @@ | |||
| 2555 | </size> | 2555 | </size> |
| 2556 | </property> | 2556 | </property> |
| 2557 | <property name="text"> | 2557 | <property name="text"> |
| 2558 | <string>Emulate Analog with Keyboard Input</string> | 2558 | <string>Mouse</string> |
| 2559 | </property> | 2559 | </property> |
| 2560 | </widget> | 2560 | </widget> |
| 2561 | </item> | 2561 | </item> |
| 2562 | <item row="2" column="0"> | 2562 | <item row="2" column="0"> |
| 2563 | <widget class="QCheckBox" name="mouse_panning"> | 2563 | <widget class="QCheckBox" name="touchscreen_enabled"> |
| 2564 | <property name="minimumSize"> | ||
| 2565 | <size> | ||
| 2566 | <width>0</width> | ||
| 2567 | <height>23</height> | ||
| 2568 | </size> | ||
| 2569 | </property> | ||
| 2570 | <property name="text"> | 2564 | <property name="text"> |
| 2571 | <string>Enable mouse panning</string> | 2565 | <string>Touchscreen</string> |
| 2572 | </property> | 2566 | </property> |
| 2573 | </widget> | 2567 | </widget> |
| 2574 | </item> | 2568 | </item> |
| 2575 | <item row="2" column="2"> | 2569 | <item row="2" column="1"> |
| 2576 | <widget class="QSpinBox" name="mouse_panning_sensitivity"> | ||
| 2577 | <property name="toolTip"> | ||
| 2578 | <string>Mouse sensitivity</string> | ||
| 2579 | </property> | ||
| 2580 | <property name="alignment"> | ||
| 2581 | <set>Qt::AlignCenter</set> | ||
| 2582 | </property> | ||
| 2583 | <property name="suffix"> | ||
| 2584 | <string>%</string> | ||
| 2585 | </property> | ||
| 2586 | <property name="minimum"> | ||
| 2587 | <number>1</number> | ||
| 2588 | </property> | ||
| 2589 | <property name="maximum"> | ||
| 2590 | <number>100</number> | ||
| 2591 | </property> | ||
| 2592 | <property name="value"> | ||
| 2593 | <number>100</number> | ||
| 2594 | </property> | ||
| 2595 | </widget> | ||
| 2596 | </item> | ||
| 2597 | <item row="6" column="2"> | ||
| 2598 | <widget class="QPushButton" name="touchscreen_advanced"> | ||
| 2599 | <property name="text"> | ||
| 2600 | <string>Advanced</string> | ||
| 2601 | </property> | ||
| 2602 | </widget> | ||
| 2603 | </item> | ||
| 2604 | <item row="3" column="1"> | ||
| 2605 | <spacer name="horizontalSpacer_8"> | 2570 | <spacer name="horizontalSpacer_8"> |
| 2606 | <property name="orientation"> | 2571 | <property name="orientation"> |
| 2607 | <enum>Qt::Horizontal</enum> | 2572 | <enum>Qt::Horizontal</enum> |
| @@ -2617,80 +2582,130 @@ | |||
| 2617 | </property> | 2582 | </property> |
| 2618 | </spacer> | 2583 | </spacer> |
| 2619 | </item> | 2584 | </item> |
| 2620 | <item row="3" column="2"> | 2585 | <item row="2" column="2"> |
| 2621 | <widget class="QPushButton" name="mouse_advanced"> | 2586 | <widget class="QPushButton" name="touchscreen_advanced"> |
| 2622 | <property name="text"> | 2587 | <property name="text"> |
| 2623 | <string>Advanced</string> | 2588 | <string>Advanced</string> |
| 2624 | </property> | 2589 | </property> |
| 2625 | </widget> | 2590 | </widget> |
| 2626 | </item> | 2591 | </item> |
| 2627 | <item row="6" column="0"> | ||
| 2628 | <widget class="QCheckBox" name="touchscreen_enabled"> | ||
| 2629 | <property name="text"> | ||
| 2630 | <string>Touchscreen</string> | ||
| 2631 | </property> | ||
| 2632 | </widget> | ||
| 2633 | </item> | ||
| 2634 | <item row="3" column="0"> | 2592 | <item row="3" column="0"> |
| 2635 | <widget class="QCheckBox" name="mouse_enabled"> | ||
| 2636 | <property name="minimumSize"> | ||
| 2637 | <size> | ||
| 2638 | <width>0</width> | ||
| 2639 | <height>23</height> | ||
| 2640 | </size> | ||
| 2641 | </property> | ||
| 2642 | <property name="text"> | ||
| 2643 | <string>Mouse</string> | ||
| 2644 | </property> | ||
| 2645 | </widget> | ||
| 2646 | </item> | ||
| 2647 | <item row="8" column="0"> | ||
| 2648 | <widget class="QLabel" name="motion_touch"> | ||
| 2649 | <property name="text"> | ||
| 2650 | <string>Motion / Touch</string> | ||
| 2651 | </property> | ||
| 2652 | </widget> | ||
| 2653 | </item> | ||
| 2654 | <item row="8" column="2"> | ||
| 2655 | <widget class="QPushButton" name="buttonMotionTouch"> | ||
| 2656 | <property name="text"> | ||
| 2657 | <string>Configure</string> | ||
| 2658 | </property> | ||
| 2659 | </widget> | ||
| 2660 | </item> | ||
| 2661 | <item row="7" column="0"> | ||
| 2662 | <widget class="QCheckBox" name="debug_enabled"> | 2593 | <widget class="QCheckBox" name="debug_enabled"> |
| 2663 | <property name="text"> | 2594 | <property name="text"> |
| 2664 | <string>Debug Controller</string> | 2595 | <string>Debug Controller</string> |
| 2665 | </property> | 2596 | </property> |
| 2666 | </widget> | 2597 | </widget> |
| 2667 | </item> | 2598 | </item> |
| 2668 | <item row="7" column="2"> | 2599 | <item row="3" column="2"> |
| 2669 | <widget class="QPushButton" name="debug_configure"> | 2600 | <widget class="QPushButton" name="debug_configure"> |
| 2670 | <property name="text"> | 2601 | <property name="text"> |
| 2671 | <string>Configure</string> | 2602 | <string>Configure</string> |
| 2672 | </property> | 2603 | </property> |
| 2673 | </widget> | 2604 | </widget> |
| 2674 | </item> | 2605 | </item> |
| 2675 | <item row="9" column="0"> | ||
| 2676 | <widget class="QCheckBox" name="enable_raw_input"> | ||
| 2677 | <property name="toolTip"> | ||
| 2678 | <string>Requires restarting yuzu</string> | ||
| 2679 | </property> | ||
| 2680 | <property name="minimumSize"> | ||
| 2681 | <size> | ||
| 2682 | <width>0</width> | ||
| 2683 | <height>23</height> | ||
| 2684 | </size> | ||
| 2685 | </property> | ||
| 2686 | <property name="text"> | ||
| 2687 | <string>Enable XInput 8 player support (disables web applet)</string> | ||
| 2688 | </property> | ||
| 2689 | </widget> | ||
| 2690 | </item> | ||
| 2691 | </layout> | 2606 | </layout> |
| 2692 | </widget> | 2607 | </widget> |
| 2693 | </item> | 2608 | </item> |
| 2609 | <item> | ||
| 2610 | <widget class="QGroupBox" name="otherGroupBox"> | ||
| 2611 | <property name="title"> | ||
| 2612 | <string>Other</string> | ||
| 2613 | </property> | ||
| 2614 | <layout class="QGridLayout" name="OtherGridLayout"> | ||
| 2615 | <item row="1" column="0"> | ||
| 2616 | <widget class="QCheckBox" name="emulate_analog_keyboard"> | ||
| 2617 | <property name="minimumSize"> | ||
| 2618 | <size> | ||
| 2619 | <width>0</width> | ||
| 2620 | <height>23</height> | ||
| 2621 | </size> | ||
| 2622 | </property> | ||
| 2623 | <property name="text"> | ||
| 2624 | <string>Emulate Analog with Keyboard Input</string> | ||
| 2625 | </property> | ||
| 2626 | </widget> | ||
| 2627 | </item> | ||
| 2628 | <item row="2" column="0"> | ||
| 2629 | <widget class="QCheckBox" name="enable_raw_input"> | ||
| 2630 | <property name="toolTip"> | ||
| 2631 | <string>Requires restarting yuzu</string> | ||
| 2632 | </property> | ||
| 2633 | <property name="minimumSize"> | ||
| 2634 | <size> | ||
| 2635 | <width>0</width> | ||
| 2636 | <height>23</height> | ||
| 2637 | </size> | ||
| 2638 | </property> | ||
| 2639 | <property name="text"> | ||
| 2640 | <string>Enable XInput 8 player support (disables web applet)</string> | ||
| 2641 | </property> | ||
| 2642 | </widget> | ||
| 2643 | </item> | ||
| 2644 | <item row="3" column="0"> | ||
| 2645 | <widget class="QCheckBox" name="enable_udp_controller"> | ||
| 2646 | <property name="minimumSize"> | ||
| 2647 | <size> | ||
| 2648 | <width>0</width> | ||
| 2649 | <height>23</height> | ||
| 2650 | </size> | ||
| 2651 | </property> | ||
| 2652 | <property name="text"> | ||
| 2653 | <string>Enable UDP controllers (not needed for motion)</string> | ||
| 2654 | </property> | ||
| 2655 | </widget> | ||
| 2656 | </item> | ||
| 2657 | <item row="4" column="0"> | ||
| 2658 | <widget class="QCheckBox" name="mouse_panning"> | ||
| 2659 | <property name="minimumSize"> | ||
| 2660 | <size> | ||
| 2661 | <width>0</width> | ||
| 2662 | <height>23</height> | ||
| 2663 | </size> | ||
| 2664 | </property> | ||
| 2665 | <property name="text"> | ||
| 2666 | <string>Enable mouse panning</string> | ||
| 2667 | </property> | ||
| 2668 | </widget> | ||
| 2669 | </item> | ||
| 2670 | <item row="4" column="2"> | ||
| 2671 | <widget class="QSpinBox" name="mouse_panning_sensitivity"> | ||
| 2672 | <property name="toolTip"> | ||
| 2673 | <string>Mouse sensitivity</string> | ||
| 2674 | </property> | ||
| 2675 | <property name="alignment"> | ||
| 2676 | <set>Qt::AlignCenter</set> | ||
| 2677 | </property> | ||
| 2678 | <property name="suffix"> | ||
| 2679 | <string>%</string> | ||
| 2680 | </property> | ||
| 2681 | <property name="minimum"> | ||
| 2682 | <number>1</number> | ||
| 2683 | </property> | ||
| 2684 | <property name="maximum"> | ||
| 2685 | <number>100</number> | ||
| 2686 | </property> | ||
| 2687 | <property name="value"> | ||
| 2688 | <number>100</number> | ||
| 2689 | </property> | ||
| 2690 | </widget> | ||
| 2691 | </item> | ||
| 2692 | <item row="5" column="0"> | ||
| 2693 | <widget class="QLabel" name="motion_touch"> | ||
| 2694 | <property name="text"> | ||
| 2695 | <string>Motion / Touch</string> | ||
| 2696 | </property> | ||
| 2697 | </widget> | ||
| 2698 | </item> | ||
| 2699 | <item row="5" column="2"> | ||
| 2700 | <widget class="QPushButton" name="buttonMotionTouch"> | ||
| 2701 | <property name="text"> | ||
| 2702 | <string>Configure</string> | ||
| 2703 | </property> | ||
| 2704 | </widget> | ||
| 2705 | </item> | ||
| 2706 | </layout> | ||
| 2707 | </widget> | ||
| 2708 | </item> | ||
| 2694 | <item> | 2709 | <item> |
| 2695 | <spacer name="verticalSpacer"> | 2710 | <spacer name="verticalSpacer"> |
| 2696 | <property name="orientation"> | 2711 | <property name="orientation"> |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 3aab5d5f8..8a8be8e40 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -12,14 +12,12 @@ | |||
| 12 | #include <QMessageBox> | 12 | #include <QMessageBox> |
| 13 | #include <QTimer> | 13 | #include <QTimer> |
| 14 | #include "common/param_package.h" | 14 | #include "common/param_package.h" |
| 15 | #include "core/core.h" | 15 | #include "core/hid/emulated_controller.h" |
| 16 | #include "core/hle/service/hid/controllers/npad.h" | 16 | #include "core/hid/hid_core.h" |
| 17 | #include "core/hle/service/hid/hid.h" | 17 | #include "core/hid/hid_types.h" |
| 18 | #include "core/hle/service/sm/sm.h" | 18 | #include "input_common/drivers/keyboard.h" |
| 19 | #include "input_common/gcadapter/gc_poller.h" | 19 | #include "input_common/drivers/mouse.h" |
| 20 | #include "input_common/main.h" | 20 | #include "input_common/main.h" |
| 21 | #include "input_common/mouse/mouse_poller.h" | ||
| 22 | #include "input_common/udp/udp.h" | ||
| 23 | #include "ui_configure_input_player.h" | 21 | #include "ui_configure_input_player.h" |
| 24 | #include "yuzu/bootmanager.h" | 22 | #include "yuzu/bootmanager.h" |
| 25 | #include "yuzu/configuration/config.h" | 23 | #include "yuzu/configuration/config.h" |
| @@ -29,8 +27,6 @@ | |||
| 29 | #include "yuzu/configuration/input_profiles.h" | 27 | #include "yuzu/configuration/input_profiles.h" |
| 30 | #include "yuzu/util/limitable_input_dialog.h" | 28 | #include "yuzu/util/limitable_input_dialog.h" |
| 31 | 29 | ||
| 32 | using namespace Service::HID; | ||
| 33 | |||
| 34 | const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> | 30 | const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> |
| 35 | ConfigureInputPlayer::analog_sub_buttons{{ | 31 | ConfigureInputPlayer::analog_sub_buttons{{ |
| 36 | "up", | 32 | "up", |
| @@ -41,33 +37,8 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> | |||
| 41 | 37 | ||
| 42 | namespace { | 38 | namespace { |
| 43 | 39 | ||
| 44 | constexpr std::size_t HANDHELD_INDEX = 8; | ||
| 45 | |||
| 46 | void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, | ||
| 47 | bool connected, Core::System& system) { | ||
| 48 | if (!system.IsPoweredOn()) { | ||
| 49 | return; | ||
| 50 | } | ||
| 51 | Service::SM::ServiceManager& sm = system.ServiceManager(); | ||
| 52 | |||
| 53 | auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>( | ||
| 54 | HidController::NPad); | ||
| 55 | |||
| 56 | npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected); | ||
| 57 | } | ||
| 58 | |||
| 59 | QString GetKeyName(int key_code) { | 40 | QString GetKeyName(int key_code) { |
| 60 | switch (key_code) { | 41 | switch (key_code) { |
| 61 | case Qt::LeftButton: | ||
| 62 | return QObject::tr("Click 0"); | ||
| 63 | case Qt::RightButton: | ||
| 64 | return QObject::tr("Click 1"); | ||
| 65 | case Qt::MiddleButton: | ||
| 66 | return QObject::tr("Click 2"); | ||
| 67 | case Qt::BackButton: | ||
| 68 | return QObject::tr("Click 3"); | ||
| 69 | case Qt::ForwardButton: | ||
| 70 | return QObject::tr("Click 4"); | ||
| 71 | case Qt::Key_Shift: | 42 | case Qt::Key_Shift: |
| 72 | return QObject::tr("Shift"); | 43 | return QObject::tr("Shift"); |
| 73 | case Qt::Key_Control: | 44 | case Qt::Key_Control: |
| @@ -81,6 +52,61 @@ QString GetKeyName(int key_code) { | |||
| 81 | } | 52 | } |
| 82 | } | 53 | } |
| 83 | 54 | ||
| 55 | QString GetButtonName(Common::Input::ButtonNames button_name) { | ||
| 56 | switch (button_name) { | ||
| 57 | case Common::Input::ButtonNames::ButtonLeft: | ||
| 58 | return QObject::tr("Left"); | ||
| 59 | case Common::Input::ButtonNames::ButtonRight: | ||
| 60 | return QObject::tr("Right"); | ||
| 61 | case Common::Input::ButtonNames::ButtonDown: | ||
| 62 | return QObject::tr("Down"); | ||
| 63 | case Common::Input::ButtonNames::ButtonUp: | ||
| 64 | return QObject::tr("Up"); | ||
| 65 | case Common::Input::ButtonNames::TriggerZ: | ||
| 66 | return QObject::tr("Z"); | ||
| 67 | case Common::Input::ButtonNames::TriggerR: | ||
| 68 | return QObject::tr("R"); | ||
| 69 | case Common::Input::ButtonNames::TriggerL: | ||
| 70 | return QObject::tr("L"); | ||
| 71 | case Common::Input::ButtonNames::ButtonA: | ||
| 72 | return QObject::tr("A"); | ||
| 73 | case Common::Input::ButtonNames::ButtonB: | ||
| 74 | return QObject::tr("B"); | ||
| 75 | case Common::Input::ButtonNames::ButtonX: | ||
| 76 | return QObject::tr("X"); | ||
| 77 | case Common::Input::ButtonNames::ButtonY: | ||
| 78 | return QObject::tr("Y"); | ||
| 79 | case Common::Input::ButtonNames::ButtonStart: | ||
| 80 | return QObject::tr("Start"); | ||
| 81 | case Common::Input::ButtonNames::L1: | ||
| 82 | return QObject::tr("L1"); | ||
| 83 | case Common::Input::ButtonNames::L2: | ||
| 84 | return QObject::tr("L2"); | ||
| 85 | case Common::Input::ButtonNames::L3: | ||
| 86 | return QObject::tr("L3"); | ||
| 87 | case Common::Input::ButtonNames::R1: | ||
| 88 | return QObject::tr("R1"); | ||
| 89 | case Common::Input::ButtonNames::R2: | ||
| 90 | return QObject::tr("R2"); | ||
| 91 | case Common::Input::ButtonNames::R3: | ||
| 92 | return QObject::tr("R3"); | ||
| 93 | case Common::Input::ButtonNames::Circle: | ||
| 94 | return QObject::tr("Circle"); | ||
| 95 | case Common::Input::ButtonNames::Cross: | ||
| 96 | return QObject::tr("Cross"); | ||
| 97 | case Common::Input::ButtonNames::Square: | ||
| 98 | return QObject::tr("Square"); | ||
| 99 | case Common::Input::ButtonNames::Triangle: | ||
| 100 | return QObject::tr("Triangle"); | ||
| 101 | case Common::Input::ButtonNames::Share: | ||
| 102 | return QObject::tr("Share"); | ||
| 103 | case Common::Input::ButtonNames::Options: | ||
| 104 | return QObject::tr("Options"); | ||
| 105 | default: | ||
| 106 | return QObject::tr("[undefined]"); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 84 | void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param, | 110 | void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param, |
| 85 | const std::string& button_name) { | 111 | const std::string& button_name) { |
| 86 | // The poller returned a complete axis, so set all the buttons | 112 | // The poller returned a complete axis, so set all the buttons |
| @@ -97,95 +123,75 @@ void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackag | |||
| 97 | } | 123 | } |
| 98 | analog_param.Set(button_name, input_param.Serialize()); | 124 | analog_param.Set(button_name, input_param.Serialize()); |
| 99 | } | 125 | } |
| 126 | } // namespace | ||
| 100 | 127 | ||
| 101 | QString ButtonToText(const Common::ParamPackage& param) { | 128 | QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { |
| 102 | if (!param.Has("engine")) { | 129 | if (!param.Has("engine")) { |
| 103 | return QObject::tr("[not set]"); | 130 | return QObject::tr("[not set]"); |
| 104 | } | 131 | } |
| 105 | 132 | ||
| 133 | const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); | ||
| 134 | const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); | ||
| 135 | const auto common_button_name = input_subsystem->GetButtonName(param); | ||
| 136 | |||
| 137 | // Retrieve the names from Qt | ||
| 106 | if (param.Get("engine", "") == "keyboard") { | 138 | if (param.Get("engine", "") == "keyboard") { |
| 107 | const QString button_str = GetKeyName(param.Get("code", 0)); | 139 | const QString button_str = GetKeyName(param.Get("code", 0)); |
| 108 | const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); | ||
| 109 | return QObject::tr("%1%2").arg(toggle, button_str); | 140 | return QObject::tr("%1%2").arg(toggle, button_str); |
| 110 | } | 141 | } |
| 111 | 142 | ||
| 112 | if (param.Get("engine", "") == "gcpad") { | 143 | if (common_button_name == Common::Input::ButtonNames::Invalid) { |
| 113 | if (param.Has("axis")) { | 144 | return QObject::tr("[invalid]"); |
| 114 | const QString axis_str = QString::fromStdString(param.Get("axis", "")); | ||
| 115 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); | ||
| 116 | |||
| 117 | return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str); | ||
| 118 | } | ||
| 119 | if (param.Has("button")) { | ||
| 120 | const QString button_str = QString::number(int(std::log2(param.Get("button", 0)))); | ||
| 121 | return QObject::tr("GC Button %1").arg(button_str); | ||
| 122 | } | ||
| 123 | return GetKeyName(param.Get("code", 0)); | ||
| 124 | } | 145 | } |
| 125 | 146 | ||
| 126 | if (param.Get("engine", "") == "tas") { | 147 | if (common_button_name == Common::Input::ButtonNames::Engine) { |
| 127 | if (param.Has("axis")) { | 148 | return QString::fromStdString(param.Get("engine", "")); |
| 128 | const QString axis_str = QString::fromStdString(param.Get("axis", "")); | ||
| 129 | |||
| 130 | return QObject::tr("TAS Axis %1").arg(axis_str); | ||
| 131 | } | ||
| 132 | if (param.Has("button")) { | ||
| 133 | const QString button_str = QString::number(int(std::log2(param.Get("button", 0)))); | ||
| 134 | return QObject::tr("TAS Btn %1").arg(button_str); | ||
| 135 | } | ||
| 136 | return GetKeyName(param.Get("code", 0)); | ||
| 137 | } | ||
| 138 | |||
| 139 | if (param.Get("engine", "") == "cemuhookudp") { | ||
| 140 | if (param.Has("pad_index")) { | ||
| 141 | const QString motion_str = QString::fromStdString(param.Get("pad_index", "")); | ||
| 142 | return QObject::tr("Motion %1").arg(motion_str); | ||
| 143 | } | ||
| 144 | return GetKeyName(param.Get("code", 0)); | ||
| 145 | } | 149 | } |
| 146 | 150 | ||
| 147 | if (param.Get("engine", "") == "sdl") { | 151 | if (common_button_name == Common::Input::ButtonNames::Value) { |
| 148 | if (param.Has("hat")) { | 152 | if (param.Has("hat")) { |
| 149 | const QString hat_str = QString::fromStdString(param.Get("hat", "")); | 153 | const QString hat = QString::fromStdString(param.Get("direction", "")); |
| 150 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); | 154 | return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat); |
| 151 | |||
| 152 | return QObject::tr("Hat %1 %2").arg(hat_str, direction_str); | ||
| 153 | } | 155 | } |
| 154 | |||
| 155 | if (param.Has("axis")) { | 156 | if (param.Has("axis")) { |
| 156 | const QString axis_str = QString::fromStdString(param.Get("axis", "")); | 157 | const QString axis = QString::fromStdString(param.Get("axis", "")); |
| 157 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); | 158 | return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis); |
| 158 | |||
| 159 | return QObject::tr("Axis %1%2").arg(axis_str, direction_str); | ||
| 160 | } | 159 | } |
| 161 | 160 | if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) { | |
| 162 | if (param.Has("button")) { | 161 | const QString axis_x = QString::fromStdString(param.Get("axis_x", "")); |
| 163 | const QString button_str = QString::fromStdString(param.Get("button", "")); | 162 | const QString axis_y = QString::fromStdString(param.Get("axis_y", "")); |
| 164 | const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); | 163 | const QString axis_z = QString::fromStdString(param.Get("axis_z", "")); |
| 165 | 164 | return QObject::tr("%1%2Axis %3,%4,%5").arg(toggle, inverted, axis_x, axis_y, axis_z); | |
| 166 | return QObject::tr("%1Button %2").arg(toggle, button_str); | ||
| 167 | } | 165 | } |
| 168 | |||
| 169 | if (param.Has("motion")) { | 166 | if (param.Has("motion")) { |
| 170 | return QObject::tr("SDL Motion"); | 167 | const QString motion = QString::fromStdString(param.Get("motion", "")); |
| 168 | return QObject::tr("%1%2Motion %3").arg(toggle, inverted, motion); | ||
| 171 | } | 169 | } |
| 172 | |||
| 173 | return {}; | ||
| 174 | } | ||
| 175 | |||
| 176 | if (param.Get("engine", "") == "mouse") { | ||
| 177 | if (param.Has("button")) { | 170 | if (param.Has("button")) { |
| 178 | const QString button_str = QString::number(int(param.Get("button", 0))); | 171 | const QString button = QString::fromStdString(param.Get("button", "")); |
| 179 | const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); | 172 | return QObject::tr("%1%2Button %3").arg(toggle, inverted, button); |
| 180 | return QObject::tr("%1Click %2").arg(toggle, button_str); | ||
| 181 | } | 173 | } |
| 182 | return GetKeyName(param.Get("code", 0)); | 174 | } |
| 175 | |||
| 176 | QString button_name = GetButtonName(common_button_name); | ||
| 177 | if (param.Has("hat")) { | ||
| 178 | return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name); | ||
| 179 | } | ||
| 180 | if (param.Has("axis")) { | ||
| 181 | return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); | ||
| 182 | } | ||
| 183 | if (param.Has("motion")) { | ||
| 184 | return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); | ||
| 185 | } | ||
| 186 | if (param.Has("button")) { | ||
| 187 | return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name); | ||
| 183 | } | 188 | } |
| 184 | 189 | ||
| 185 | return QObject::tr("[unknown]"); | 190 | return QObject::tr("[unknown]"); |
| 186 | } | 191 | } |
| 187 | 192 | ||
| 188 | QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { | 193 | QString ConfigureInputPlayer::AnalogToText(const Common::ParamPackage& param, |
| 194 | const std::string& dir) { | ||
| 189 | if (!param.Has("engine")) { | 195 | if (!param.Has("engine")) { |
| 190 | return QObject::tr("[not set]"); | 196 | return QObject::tr("[not set]"); |
| 191 | } | 197 | } |
| @@ -194,49 +200,69 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) | |||
| 194 | return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); | 200 | return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); |
| 195 | } | 201 | } |
| 196 | 202 | ||
| 203 | if (!param.Has("axis_x") || !param.Has("axis_y")) { | ||
| 204 | return QObject::tr("[unknown]"); | ||
| 205 | } | ||
| 206 | |||
| 197 | const auto engine_str = param.Get("engine", ""); | 207 | const auto engine_str = param.Get("engine", ""); |
| 198 | const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); | 208 | const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); |
| 199 | const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); | 209 | const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); |
| 200 | const bool invert_x = param.Get("invert_x", "+") == "-"; | 210 | const bool invert_x = param.Get("invert_x", "+") == "-"; |
| 201 | const bool invert_y = param.Get("invert_y", "+") == "-"; | 211 | const bool invert_y = param.Get("invert_y", "+") == "-"; |
| 202 | if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse" || | ||
| 203 | engine_str == "tas") { | ||
| 204 | if (dir == "modifier") { | ||
| 205 | return QObject::tr("[unused]"); | ||
| 206 | } | ||
| 207 | 212 | ||
| 208 | if (dir == "left") { | 213 | if (dir == "modifier") { |
| 209 | const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-"); | 214 | return QObject::tr("[unused]"); |
| 210 | return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); | 215 | } |
| 211 | } | ||
| 212 | if (dir == "right") { | ||
| 213 | const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+"); | ||
| 214 | return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); | ||
| 215 | } | ||
| 216 | if (dir == "up") { | ||
| 217 | const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+"); | ||
| 218 | return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str); | ||
| 219 | } | ||
| 220 | if (dir == "down") { | ||
| 221 | const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-"); | ||
| 222 | return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str); | ||
| 223 | } | ||
| 224 | 216 | ||
| 225 | return {}; | 217 | if (dir == "left") { |
| 218 | const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-"); | ||
| 219 | return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); | ||
| 226 | } | 220 | } |
| 221 | if (dir == "right") { | ||
| 222 | const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+"); | ||
| 223 | return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); | ||
| 224 | } | ||
| 225 | if (dir == "up") { | ||
| 226 | const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+"); | ||
| 227 | return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str); | ||
| 228 | } | ||
| 229 | if (dir == "down") { | ||
| 230 | const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-"); | ||
| 231 | return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str); | ||
| 232 | } | ||
| 233 | |||
| 227 | return QObject::tr("[unknown]"); | 234 | return QObject::tr("[unknown]"); |
| 228 | } | 235 | } |
| 229 | } // namespace | ||
| 230 | 236 | ||
| 231 | ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, | 237 | ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, |
| 232 | QWidget* bottom_row, | 238 | QWidget* bottom_row, |
| 233 | InputCommon::InputSubsystem* input_subsystem_, | 239 | InputCommon::InputSubsystem* input_subsystem_, |
| 234 | InputProfiles* profiles_, Core::System& system_, | 240 | InputProfiles* profiles_, Core::HID::HIDCore& hid_core_, |
| 235 | bool debug) | 241 | bool is_powered_on_, bool debug) |
| 236 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), | 242 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), |
| 237 | debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_), | 243 | debug(debug), is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_}, |
| 238 | timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()), | 244 | profiles(profiles_), timeout_timer(std::make_unique<QTimer>()), |
| 239 | bottom_row(bottom_row), system{system_} { | 245 | poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row), hid_core{hid_core_} { |
| 246 | if (player_index == 0) { | ||
| 247 | auto* emulated_controller_p1 = | ||
| 248 | hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 249 | auto* emulated_controller_handheld = | ||
| 250 | hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 251 | emulated_controller_p1->SaveCurrentConfig(); | ||
| 252 | emulated_controller_p1->EnableConfiguration(); | ||
| 253 | emulated_controller_handheld->SaveCurrentConfig(); | ||
| 254 | emulated_controller_handheld->EnableConfiguration(); | ||
| 255 | if (emulated_controller_handheld->IsConnected(true)) { | ||
| 256 | emulated_controller_p1->Disconnect(); | ||
| 257 | emulated_controller = emulated_controller_handheld; | ||
| 258 | } else { | ||
| 259 | emulated_controller = emulated_controller_p1; | ||
| 260 | } | ||
| 261 | } else { | ||
| 262 | emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index); | ||
| 263 | emulated_controller->SaveCurrentConfig(); | ||
| 264 | emulated_controller->EnableConfiguration(); | ||
| 265 | } | ||
| 240 | ui->setupUi(this); | 266 | ui->setupUi(this); |
| 241 | 267 | ||
| 242 | setFocusPolicy(Qt::ClickFocus); | 268 | setFocusPolicy(Qt::ClickFocus); |
| @@ -278,31 +304,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 278 | analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup}; | 304 | analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup}; |
| 279 | analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; | 305 | analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; |
| 280 | 306 | ||
| 281 | const auto ConfigureButtonClick = [&](QPushButton* button, std::size_t button_id, | 307 | ui->controllerFrame->SetController(emulated_controller); |
| 282 | Common::ParamPackage* param, int default_val, | ||
| 283 | InputCommon::Polling::DeviceType type) { | ||
| 284 | connect(button, &QPushButton::clicked, [=, this] { | ||
| 285 | HandleClick( | ||
| 286 | button, button_id, | ||
| 287 | [=, this](Common::ParamPackage params) { | ||
| 288 | // Workaround for ZL & ZR for analog triggers like on XBOX | ||
| 289 | // controllers. Analog triggers (from controllers like the XBOX | ||
| 290 | // controller) would not work due to a different range of their | ||
| 291 | // signals (from 0 to 255 on analog triggers instead of -32768 to | ||
| 292 | // 32768 on analog joysticks). The SDL driver misinterprets analog | ||
| 293 | // triggers as analog joysticks. | ||
| 294 | // TODO: reinterpret the signal range for analog triggers to map the | ||
| 295 | // values correctly. This is required for the correct emulation of | ||
| 296 | // the analog triggers of the GameCube controller. | ||
| 297 | if (button == ui->buttonZL || button == ui->buttonZR) { | ||
| 298 | params.Set("direction", "+"); | ||
| 299 | params.Set("threshold", "0.5"); | ||
| 300 | } | ||
| 301 | *param = std::move(params); | ||
| 302 | }, | ||
| 303 | type); | ||
| 304 | }); | ||
| 305 | }; | ||
| 306 | 308 | ||
| 307 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { | 309 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { |
| 308 | auto* const button = button_map[button_id]; | 310 | auto* const button = button_map[button_id]; |
| @@ -311,34 +313,52 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 311 | continue; | 313 | continue; |
| 312 | } | 314 | } |
| 313 | 315 | ||
| 314 | ConfigureButtonClick(button_map[button_id], button_id, &buttons_param[button_id], | 316 | connect(button, &QPushButton::clicked, [=, this] { |
| 315 | Config::default_buttons[button_id], | 317 | HandleClick( |
| 316 | InputCommon::Polling::DeviceType::Button); | 318 | button, button_id, |
| 319 | [=, this](Common::ParamPackage params) { | ||
| 320 | emulated_controller->SetButtonParam(button_id, params); | ||
| 321 | }, | ||
| 322 | InputCommon::Polling::InputType::Button); | ||
| 323 | }); | ||
| 317 | 324 | ||
| 318 | button->setContextMenuPolicy(Qt::CustomContextMenu); | 325 | button->setContextMenuPolicy(Qt::CustomContextMenu); |
| 319 | connect(button, &QPushButton::customContextMenuRequested, | 326 | connect(button, &QPushButton::customContextMenuRequested, |
| 320 | [=, this](const QPoint& menu_location) { | 327 | [=, this](const QPoint& menu_location) { |
| 321 | QMenu context_menu; | 328 | QMenu context_menu; |
| 329 | Common::ParamPackage param = emulated_controller->GetButtonParam(button_id); | ||
| 322 | context_menu.addAction(tr("Clear"), [&] { | 330 | context_menu.addAction(tr("Clear"), [&] { |
| 323 | buttons_param[button_id].Clear(); | 331 | emulated_controller->SetButtonParam(button_id, {}); |
| 324 | button_map[button_id]->setText(tr("[not set]")); | 332 | button_map[button_id]->setText(tr("[not set]")); |
| 325 | }); | 333 | }); |
| 326 | if (buttons_param[button_id].Has("toggle")) { | 334 | if (param.Has("button") || param.Has("hat")) { |
| 327 | context_menu.addAction(tr("Toggle button"), [&] { | 335 | context_menu.addAction(tr("Toggle button"), [&] { |
| 328 | const bool toggle_value = | 336 | const bool toggle_value = !param.Get("toggle", false); |
| 329 | !buttons_param[button_id].Get("toggle", false); | 337 | param.Set("toggle", toggle_value); |
| 330 | buttons_param[button_id].Set("toggle", toggle_value); | 338 | button_map[button_id]->setText(ButtonToText(param)); |
| 331 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | 339 | emulated_controller->SetButtonParam(button_id, param); |
| 340 | }); | ||
| 341 | context_menu.addAction(tr("Invert button"), [&] { | ||
| 342 | const bool toggle_value = !param.Get("inverted", false); | ||
| 343 | param.Set("inverted", toggle_value); | ||
| 344 | button_map[button_id]->setText(ButtonToText(param)); | ||
| 345 | emulated_controller->SetButtonParam(button_id, param); | ||
| 332 | }); | 346 | }); |
| 333 | } | 347 | } |
| 334 | if (buttons_param[button_id].Has("threshold")) { | 348 | if (param.Has("axis")) { |
| 349 | context_menu.addAction(tr("Invert axis"), [&] { | ||
| 350 | const bool toggle_value = !(param.Get("invert", "+") == "-"); | ||
| 351 | param.Set("invert", toggle_value ? "-" : "+"); | ||
| 352 | button_map[button_id]->setText(ButtonToText(param)); | ||
| 353 | emulated_controller->SetButtonParam(button_id, param); | ||
| 354 | }); | ||
| 335 | context_menu.addAction(tr("Set threshold"), [&] { | 355 | context_menu.addAction(tr("Set threshold"), [&] { |
| 336 | const int button_threshold = static_cast<int>( | 356 | const int button_threshold = |
| 337 | buttons_param[button_id].Get("threshold", 0.5f) * 100.0f); | 357 | static_cast<int>(param.Get("threshold", 0.5f) * 100.0f); |
| 338 | const int new_threshold = QInputDialog::getInt( | 358 | const int new_threshold = QInputDialog::getInt( |
| 339 | this, tr("Set threshold"), tr("Choose a value between 0% and 100%"), | 359 | this, tr("Set threshold"), tr("Choose a value between 0% and 100%"), |
| 340 | button_threshold, 0, 100); | 360 | button_threshold, 0, 100); |
| 341 | buttons_param[button_id].Set("threshold", new_threshold / 100.0f); | 361 | param.Set("threshold", new_threshold / 100.0f); |
| 342 | 362 | ||
| 343 | if (button_id == Settings::NativeButton::ZL) { | 363 | if (button_id == Settings::NativeButton::ZL) { |
| 344 | ui->sliderZLThreshold->setValue(new_threshold); | 364 | ui->sliderZLThreshold->setValue(new_threshold); |
| @@ -346,11 +366,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 346 | if (button_id == Settings::NativeButton::ZR) { | 366 | if (button_id == Settings::NativeButton::ZR) { |
| 347 | ui->sliderZRThreshold->setValue(new_threshold); | 367 | ui->sliderZRThreshold->setValue(new_threshold); |
| 348 | } | 368 | } |
| 369 | emulated_controller->SetButtonParam(button_id, param); | ||
| 349 | }); | 370 | }); |
| 350 | } | 371 | } |
| 351 | |||
| 352 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | 372 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); |
| 353 | ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); | ||
| 354 | }); | 373 | }); |
| 355 | } | 374 | } |
| 356 | 375 | ||
| @@ -360,9 +379,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 360 | continue; | 379 | continue; |
| 361 | } | 380 | } |
| 362 | 381 | ||
| 363 | ConfigureButtonClick(motion_map[motion_id], motion_id, &motions_param[motion_id], | 382 | connect(button, &QPushButton::clicked, [=, this] { |
| 364 | Config::default_motions[motion_id], | 383 | HandleClick( |
| 365 | InputCommon::Polling::DeviceType::Motion); | 384 | button, motion_id, |
| 385 | [=, this](Common::ParamPackage params) { | ||
| 386 | emulated_controller->SetMotionParam(motion_id, params); | ||
| 387 | }, | ||
| 388 | InputCommon::Polling::InputType::Motion); | ||
| 389 | }); | ||
| 366 | 390 | ||
| 367 | button->setContextMenuPolicy(Qt::CustomContextMenu); | 391 | button->setContextMenuPolicy(Qt::CustomContextMenu); |
| 368 | 392 | ||
| @@ -370,7 +394,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 370 | [=, this](const QPoint& menu_location) { | 394 | [=, this](const QPoint& menu_location) { |
| 371 | QMenu context_menu; | 395 | QMenu context_menu; |
| 372 | context_menu.addAction(tr("Clear"), [&] { | 396 | context_menu.addAction(tr("Clear"), [&] { |
| 373 | motions_param[motion_id].Clear(); | 397 | emulated_controller->SetMotionParam(motion_id, {}); |
| 374 | motion_map[motion_id]->setText(tr("[not set]")); | 398 | motion_map[motion_id]->setText(tr("[not set]")); |
| 375 | }); | 399 | }); |
| 376 | context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location)); | 400 | context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location)); |
| @@ -378,16 +402,22 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 378 | } | 402 | } |
| 379 | 403 | ||
| 380 | connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] { | 404 | connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] { |
| 381 | if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) { | 405 | Common::ParamPackage param = |
| 406 | emulated_controller->GetButtonParam(Settings::NativeButton::ZL); | ||
| 407 | if (param.Has("threshold")) { | ||
| 382 | const auto slider_value = ui->sliderZLThreshold->value(); | 408 | const auto slider_value = ui->sliderZLThreshold->value(); |
| 383 | buttons_param[Settings::NativeButton::ZL].Set("threshold", slider_value / 100.0f); | 409 | param.Set("threshold", slider_value / 100.0f); |
| 410 | emulated_controller->SetButtonParam(Settings::NativeButton::ZL, param); | ||
| 384 | } | 411 | } |
| 385 | }); | 412 | }); |
| 386 | 413 | ||
| 387 | connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] { | 414 | connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] { |
| 388 | if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) { | 415 | Common::ParamPackage param = |
| 416 | emulated_controller->GetButtonParam(Settings::NativeButton::ZR); | ||
| 417 | if (param.Has("threshold")) { | ||
| 389 | const auto slider_value = ui->sliderZRThreshold->value(); | 418 | const auto slider_value = ui->sliderZRThreshold->value(); |
| 390 | buttons_param[Settings::NativeButton::ZR].Set("threshold", slider_value / 100.0f); | 419 | param.Set("threshold", slider_value / 100.0f); |
| 420 | emulated_controller->SetButtonParam(Settings::NativeButton::ZR, param); | ||
| 391 | } | 421 | } |
| 392 | }); | 422 | }); |
| 393 | 423 | ||
| @@ -415,45 +445,45 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 415 | HandleClick( | 445 | HandleClick( |
| 416 | analog_map_buttons[analog_id][sub_button_id], analog_id, | 446 | analog_map_buttons[analog_id][sub_button_id], analog_id, |
| 417 | [=, this](const Common::ParamPackage& params) { | 447 | [=, this](const Common::ParamPackage& params) { |
| 418 | SetAnalogParam(params, analogs_param[analog_id], | 448 | Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); |
| 419 | analog_sub_buttons[sub_button_id]); | 449 | SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]); |
| 450 | emulated_controller->SetStickParam(analog_id, param); | ||
| 420 | }, | 451 | }, |
| 421 | InputCommon::Polling::DeviceType::AnalogPreferred); | 452 | InputCommon::Polling::InputType::Stick); |
| 422 | }); | 453 | }); |
| 423 | 454 | ||
| 424 | analog_button->setContextMenuPolicy(Qt::CustomContextMenu); | 455 | analog_button->setContextMenuPolicy(Qt::CustomContextMenu); |
| 425 | 456 | ||
| 426 | connect( | 457 | connect(analog_button, &QPushButton::customContextMenuRequested, |
| 427 | analog_button, &QPushButton::customContextMenuRequested, | 458 | [=, this](const QPoint& menu_location) { |
| 428 | [=, this](const QPoint& menu_location) { | 459 | QMenu context_menu; |
| 429 | QMenu context_menu; | 460 | Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); |
| 430 | context_menu.addAction(tr("Clear"), [&] { | 461 | context_menu.addAction(tr("Clear"), [&] { |
| 431 | analogs_param[analog_id].Clear(); | 462 | emulated_controller->SetStickParam(analog_id, {}); |
| 432 | analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); | 463 | analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); |
| 433 | }); | 464 | }); |
| 434 | context_menu.addAction(tr("Invert axis"), [&] { | 465 | context_menu.addAction(tr("Invert axis"), [&] { |
| 435 | if (sub_button_id == 2 || sub_button_id == 3) { | 466 | if (sub_button_id == 2 || sub_button_id == 3) { |
| 436 | const bool invert_value = | 467 | const bool invert_value = param.Get("invert_x", "+") == "-"; |
| 437 | analogs_param[analog_id].Get("invert_x", "+") == "-"; | 468 | const std::string invert_str = invert_value ? "+" : "-"; |
| 438 | const std::string invert_str = invert_value ? "+" : "-"; | 469 | param.Set("invert_x", invert_str); |
| 439 | analogs_param[analog_id].Set("invert_x", invert_str); | 470 | emulated_controller->SetStickParam(analog_id, param); |
| 440 | } | 471 | } |
| 441 | if (sub_button_id == 0 || sub_button_id == 1) { | 472 | if (sub_button_id == 0 || sub_button_id == 1) { |
| 442 | const bool invert_value = | 473 | const bool invert_value = param.Get("invert_y", "+") == "-"; |
| 443 | analogs_param[analog_id].Get("invert_y", "+") == "-"; | 474 | const std::string invert_str = invert_value ? "+" : "-"; |
| 444 | const std::string invert_str = invert_value ? "+" : "-"; | 475 | param.Set("invert_y", invert_str); |
| 445 | analogs_param[analog_id].Set("invert_y", invert_str); | 476 | emulated_controller->SetStickParam(analog_id, param); |
| 446 | } | 477 | } |
| 447 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; | 478 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; |
| 448 | ++sub_button_id) { | 479 | ++sub_button_id) { |
| 449 | analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( | 480 | analog_map_buttons[analog_id][sub_button_id]->setText( |
| 450 | analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | 481 | AnalogToText(param, analog_sub_buttons[sub_button_id])); |
| 451 | } | 482 | } |
| 483 | }); | ||
| 484 | context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal( | ||
| 485 | menu_location)); | ||
| 452 | }); | 486 | }); |
| 453 | context_menu.exec( | ||
| 454 | analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(menu_location)); | ||
| 455 | ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); | ||
| 456 | }); | ||
| 457 | } | 487 | } |
| 458 | 488 | ||
| 459 | // Handle clicks for the modifier buttons as well. | 489 | // Handle clicks for the modifier buttons as well. |
| @@ -461,9 +491,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 461 | HandleClick( | 491 | HandleClick( |
| 462 | analog_map_modifier_button[analog_id], analog_id, | 492 | analog_map_modifier_button[analog_id], analog_id, |
| 463 | [=, this](const Common::ParamPackage& params) { | 493 | [=, this](const Common::ParamPackage& params) { |
| 464 | analogs_param[analog_id].Set("modifier", params.Serialize()); | 494 | Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); |
| 495 | param.Set("modifier", params.Serialize()); | ||
| 496 | emulated_controller->SetStickParam(analog_id, param); | ||
| 465 | }, | 497 | }, |
| 466 | InputCommon::Polling::DeviceType::Button); | 498 | InputCommon::Polling::InputType::Button); |
| 467 | }); | 499 | }); |
| 468 | 500 | ||
| 469 | analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu); | 501 | analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu); |
| @@ -471,18 +503,21 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 471 | connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested, | 503 | connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested, |
| 472 | [=, this](const QPoint& menu_location) { | 504 | [=, this](const QPoint& menu_location) { |
| 473 | QMenu context_menu; | 505 | QMenu context_menu; |
| 506 | Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); | ||
| 474 | context_menu.addAction(tr("Clear"), [&] { | 507 | context_menu.addAction(tr("Clear"), [&] { |
| 475 | analogs_param[analog_id].Set("modifier", ""); | 508 | param.Set("modifier", ""); |
| 476 | analog_map_modifier_button[analog_id]->setText(tr("[not set]")); | 509 | analog_map_modifier_button[analog_id]->setText(tr("[not set]")); |
| 510 | emulated_controller->SetStickParam(analog_id, param); | ||
| 477 | }); | 511 | }); |
| 478 | context_menu.addAction(tr("Toggle button"), [&] { | 512 | context_menu.addAction(tr("Toggle button"), [&] { |
| 479 | Common::ParamPackage modifier_param = | 513 | Common::ParamPackage modifier_param = |
| 480 | Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")}; | 514 | Common::ParamPackage{param.Get("modifier", "")}; |
| 481 | const bool toggle_value = !modifier_param.Get("toggle", false); | 515 | const bool toggle_value = !modifier_param.Get("toggle", false); |
| 482 | modifier_param.Set("toggle", toggle_value); | 516 | modifier_param.Set("toggle", toggle_value); |
| 483 | analogs_param[analog_id].Set("modifier", modifier_param.Serialize()); | 517 | param.Set("modifier", modifier_param.Serialize()); |
| 484 | analog_map_modifier_button[analog_id]->setText( | 518 | analog_map_modifier_button[analog_id]->setText( |
| 485 | ButtonToText(modifier_param)); | 519 | ButtonToText(modifier_param)); |
| 520 | emulated_controller->SetStickParam(analog_id, param); | ||
| 486 | }); | 521 | }); |
| 487 | context_menu.exec( | 522 | context_menu.exec( |
| 488 | analog_map_modifier_button[analog_id]->mapToGlobal(menu_location)); | 523 | analog_map_modifier_button[analog_id]->mapToGlobal(menu_location)); |
| @@ -490,37 +525,39 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 490 | 525 | ||
| 491 | connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged), | 526 | connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged), |
| 492 | [=, this] { | 527 | [=, this] { |
| 528 | Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); | ||
| 493 | const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); | 529 | const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); |
| 494 | analogs_param[analog_id].Set("range", spinbox_value / 100.0f); | 530 | param.Set("range", spinbox_value / 100.0f); |
| 495 | ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); | 531 | emulated_controller->SetStickParam(analog_id, param); |
| 496 | }); | 532 | }); |
| 497 | 533 | ||
| 498 | connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] { | 534 | connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] { |
| 535 | Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); | ||
| 499 | const auto slider_value = analog_map_deadzone_slider[analog_id]->value(); | 536 | const auto slider_value = analog_map_deadzone_slider[analog_id]->value(); |
| 500 | analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value)); | 537 | analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value)); |
| 501 | analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); | 538 | param.Set("deadzone", slider_value / 100.0f); |
| 502 | ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); | 539 | emulated_controller->SetStickParam(analog_id, param); |
| 503 | }); | 540 | }); |
| 504 | 541 | ||
| 505 | connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] { | 542 | connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] { |
| 543 | Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); | ||
| 506 | const auto slider_value = analog_map_modifier_slider[analog_id]->value(); | 544 | const auto slider_value = analog_map_modifier_slider[analog_id]->value(); |
| 507 | analog_map_modifier_label[analog_id]->setText( | 545 | analog_map_modifier_label[analog_id]->setText( |
| 508 | tr("Modifier Range: %1%").arg(slider_value)); | 546 | tr("Modifier Range: %1%").arg(slider_value)); |
| 509 | analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); | 547 | param.Set("modifier_scale", slider_value / 100.0f); |
| 548 | emulated_controller->SetStickParam(analog_id, param); | ||
| 510 | }); | 549 | }); |
| 511 | } | 550 | } |
| 512 | 551 | ||
| 513 | // Player Connected checkbox | 552 | // Player Connected checkbox |
| 514 | connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) { | 553 | connect(ui->groupConnectedController, &QGroupBox::toggled, |
| 515 | emit Connected(checked); | 554 | [this](bool checked) { emit Connected(checked); }); |
| 516 | ui->controllerFrame->SetConnectedStatus(checked); | ||
| 517 | }); | ||
| 518 | 555 | ||
| 519 | if (player_index == 0) { | 556 | if (player_index == 0) { |
| 520 | connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), | 557 | connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), |
| 521 | [this](int index) { | 558 | [this](int index) { |
| 522 | emit HandheldStateChanged(GetControllerTypeFromIndex(index) == | 559 | emit HandheldStateChanged(GetControllerTypeFromIndex(index) == |
| 523 | Settings::ControllerType::Handheld); | 560 | Core::HID::NpadStyleIndex::Handheld); |
| 524 | }); | 561 | }); |
| 525 | } | 562 | } |
| 526 | 563 | ||
| @@ -537,18 +574,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 537 | SetConnectableControllers(); | 574 | SetConnectableControllers(); |
| 538 | } | 575 | } |
| 539 | 576 | ||
| 540 | UpdateControllerIcon(); | ||
| 541 | UpdateControllerAvailableButtons(); | 577 | UpdateControllerAvailableButtons(); |
| 542 | UpdateControllerEnabledButtons(); | 578 | UpdateControllerEnabledButtons(); |
| 543 | UpdateControllerButtonNames(); | 579 | UpdateControllerButtonNames(); |
| 544 | UpdateMotionButtons(); | 580 | UpdateMotionButtons(); |
| 545 | connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) { | 581 | connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), |
| 546 | UpdateControllerIcon(); | 582 | [this, player_index](int) { |
| 547 | UpdateControllerAvailableButtons(); | 583 | UpdateControllerAvailableButtons(); |
| 548 | UpdateControllerEnabledButtons(); | 584 | UpdateControllerEnabledButtons(); |
| 549 | UpdateControllerButtonNames(); | 585 | UpdateControllerButtonNames(); |
| 550 | UpdateMotionButtons(); | 586 | UpdateMotionButtons(); |
| 551 | }); | 587 | const Core::HID::NpadStyleIndex type = |
| 588 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | ||
| 589 | |||
| 590 | if (player_index == 0) { | ||
| 591 | auto* emulated_controller_p1 = | ||
| 592 | hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 593 | auto* emulated_controller_handheld = | ||
| 594 | hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 595 | bool is_connected = emulated_controller->IsConnected(true); | ||
| 596 | |||
| 597 | emulated_controller_p1->SetNpadStyleIndex(type); | ||
| 598 | emulated_controller_handheld->SetNpadStyleIndex(type); | ||
| 599 | if (is_connected) { | ||
| 600 | if (type == Core::HID::NpadStyleIndex::Handheld) { | ||
| 601 | emulated_controller_p1->Disconnect(); | ||
| 602 | emulated_controller_handheld->Connect(); | ||
| 603 | emulated_controller = emulated_controller_handheld; | ||
| 604 | } else { | ||
| 605 | emulated_controller_handheld->Disconnect(); | ||
| 606 | emulated_controller_p1->Connect(); | ||
| 607 | emulated_controller = emulated_controller_p1; | ||
| 608 | } | ||
| 609 | } | ||
| 610 | ui->controllerFrame->SetController(emulated_controller); | ||
| 611 | } | ||
| 612 | emulated_controller->SetNpadStyleIndex(type); | ||
| 613 | }); | ||
| 552 | 614 | ||
| 553 | connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, | 615 | connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, |
| 554 | &ConfigureInputPlayer::UpdateMappingWithDefaults); | 616 | &ConfigureInputPlayer::UpdateMappingWithDefaults); |
| @@ -563,62 +625,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 563 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); | 625 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); |
| 564 | 626 | ||
| 565 | connect(poll_timer.get(), &QTimer::timeout, [this] { | 627 | connect(poll_timer.get(), &QTimer::timeout, [this] { |
| 566 | Common::ParamPackage params; | 628 | const auto& params = input_subsystem->GetNextInput(); |
| 567 | if (input_subsystem->GetGCButtons()->IsPolling()) { | 629 | if (params.Has("engine") && IsInputAcceptable(params)) { |
| 568 | params = input_subsystem->GetGCButtons()->GetNextInput(); | 630 | SetPollingResult(params, false); |
| 569 | if (params.Has("engine") && IsInputAcceptable(params)) { | 631 | return; |
| 570 | SetPollingResult(params, false); | ||
| 571 | return; | ||
| 572 | } | ||
| 573 | } | ||
| 574 | if (input_subsystem->GetGCAnalogs()->IsPolling()) { | ||
| 575 | params = input_subsystem->GetGCAnalogs()->GetNextInput(); | ||
| 576 | if (params.Has("engine") && IsInputAcceptable(params)) { | ||
| 577 | SetPollingResult(params, false); | ||
| 578 | return; | ||
| 579 | } | ||
| 580 | } | ||
| 581 | if (input_subsystem->GetUDPMotions()->IsPolling()) { | ||
| 582 | params = input_subsystem->GetUDPMotions()->GetNextInput(); | ||
| 583 | if (params.Has("engine")) { | ||
| 584 | SetPollingResult(params, false); | ||
| 585 | return; | ||
| 586 | } | ||
| 587 | } | ||
| 588 | if (input_subsystem->GetMouseButtons()->IsPolling()) { | ||
| 589 | params = input_subsystem->GetMouseButtons()->GetNextInput(); | ||
| 590 | if (params.Has("engine") && IsInputAcceptable(params)) { | ||
| 591 | SetPollingResult(params, false); | ||
| 592 | return; | ||
| 593 | } | ||
| 594 | } | ||
| 595 | if (input_subsystem->GetMouseAnalogs()->IsPolling()) { | ||
| 596 | params = input_subsystem->GetMouseAnalogs()->GetNextInput(); | ||
| 597 | if (params.Has("engine") && IsInputAcceptable(params)) { | ||
| 598 | SetPollingResult(params, false); | ||
| 599 | return; | ||
| 600 | } | ||
| 601 | } | ||
| 602 | if (input_subsystem->GetMouseMotions()->IsPolling()) { | ||
| 603 | params = input_subsystem->GetMouseMotions()->GetNextInput(); | ||
| 604 | if (params.Has("engine") && IsInputAcceptable(params)) { | ||
| 605 | SetPollingResult(params, false); | ||
| 606 | return; | ||
| 607 | } | ||
| 608 | } | ||
| 609 | if (input_subsystem->GetMouseTouch()->IsPolling()) { | ||
| 610 | params = input_subsystem->GetMouseTouch()->GetNextInput(); | ||
| 611 | if (params.Has("engine") && IsInputAcceptable(params)) { | ||
| 612 | SetPollingResult(params, false); | ||
| 613 | return; | ||
| 614 | } | ||
| 615 | } | ||
| 616 | for (auto& poller : device_pollers) { | ||
| 617 | params = poller->GetNextInput(); | ||
| 618 | if (params.Has("engine") && IsInputAcceptable(params)) { | ||
| 619 | SetPollingResult(params, false); | ||
| 620 | return; | ||
| 621 | } | ||
| 622 | } | 632 | } |
| 623 | }); | 633 | }); |
| 624 | 634 | ||
| @@ -634,110 +644,38 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 634 | &ConfigureInputPlayer::SaveProfile); | 644 | &ConfigureInputPlayer::SaveProfile); |
| 635 | 645 | ||
| 636 | LoadConfiguration(); | 646 | LoadConfiguration(); |
| 637 | ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); | ||
| 638 | ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked()); | ||
| 639 | } | 647 | } |
| 640 | 648 | ||
| 641 | ConfigureInputPlayer::~ConfigureInputPlayer() = default; | 649 | ConfigureInputPlayer::~ConfigureInputPlayer() { |
| 642 | |||
| 643 | void ConfigureInputPlayer::ApplyConfiguration() { | ||
| 644 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 645 | auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons; | ||
| 646 | auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs; | ||
| 647 | |||
| 648 | std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(), | ||
| 649 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 650 | std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(), | ||
| 651 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 652 | |||
| 653 | if (debug) { | ||
| 654 | return; | ||
| 655 | } | ||
| 656 | |||
| 657 | auto& motions = player.motions; | ||
| 658 | |||
| 659 | std::transform(motions_param.begin(), motions_param.end(), motions.begin(), | ||
| 660 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 661 | |||
| 662 | // Apply configuration for handheld | ||
| 663 | if (player_index == 0) { | 650 | if (player_index == 0) { |
| 664 | auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; | 651 | auto* emulated_controller_p1 = |
| 665 | const auto handheld_connected = handheld.connected; | 652 | hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 666 | handheld = player; | 653 | auto* emulated_controller_handheld = |
| 667 | handheld.connected = handheld_connected; | 654 | hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 655 | emulated_controller_p1->DisableConfiguration(); | ||
| 656 | emulated_controller_handheld->DisableConfiguration(); | ||
| 657 | } else { | ||
| 658 | emulated_controller->DisableConfiguration(); | ||
| 668 | } | 659 | } |
| 669 | } | 660 | } |
| 670 | 661 | ||
| 671 | void ConfigureInputPlayer::TryConnectSelectedController() { | 662 | void ConfigureInputPlayer::ApplyConfiguration() { |
| 672 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 673 | |||
| 674 | const auto controller_type = | ||
| 675 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | ||
| 676 | const auto player_connected = ui->groupConnectedController->isChecked() && | ||
| 677 | controller_type != Settings::ControllerType::Handheld; | ||
| 678 | |||
| 679 | // Connect Handheld depending on Player 1's controller configuration. | ||
| 680 | if (player_index == 0) { | 663 | if (player_index == 0) { |
| 681 | auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; | 664 | auto* emulated_controller_p1 = |
| 682 | const auto handheld_connected = ui->groupConnectedController->isChecked() && | 665 | hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 683 | controller_type == Settings::ControllerType::Handheld; | 666 | auto* emulated_controller_handheld = |
| 684 | // Connect only if handheld is going from disconnected to connected | 667 | hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 685 | if (!handheld.connected && handheld_connected) { | 668 | emulated_controller_p1->DisableConfiguration(); |
| 686 | UpdateController(controller_type, HANDHELD_INDEX, true, system); | 669 | emulated_controller_p1->SaveCurrentConfig(); |
| 687 | } | 670 | emulated_controller_p1->EnableConfiguration(); |
| 688 | handheld.connected = handheld_connected; | 671 | emulated_controller_handheld->DisableConfiguration(); |
| 689 | } | 672 | emulated_controller_handheld->SaveCurrentConfig(); |
| 690 | 673 | emulated_controller_handheld->EnableConfiguration(); | |
| 691 | if (player.controller_type == controller_type && player.connected == player_connected) { | ||
| 692 | // Set vibration devices in the event that the input device has changed. | ||
| 693 | ConfigureVibration::SetVibrationDevices(player_index); | ||
| 694 | return; | 674 | return; |
| 695 | } | 675 | } |
| 696 | 676 | emulated_controller->DisableConfiguration(); | |
| 697 | player.controller_type = controller_type; | 677 | emulated_controller->SaveCurrentConfig(); |
| 698 | player.connected = player_connected; | 678 | emulated_controller->EnableConfiguration(); |
| 699 | |||
| 700 | ConfigureVibration::SetVibrationDevices(player_index); | ||
| 701 | |||
| 702 | if (!player.connected) { | ||
| 703 | return; | ||
| 704 | } | ||
| 705 | |||
| 706 | UpdateController(controller_type, player_index, true, system); | ||
| 707 | } | ||
| 708 | |||
| 709 | void ConfigureInputPlayer::TryDisconnectSelectedController() { | ||
| 710 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 711 | |||
| 712 | const auto controller_type = | ||
| 713 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | ||
| 714 | const auto player_connected = ui->groupConnectedController->isChecked() && | ||
| 715 | controller_type != Settings::ControllerType::Handheld; | ||
| 716 | |||
| 717 | // Disconnect Handheld depending on Player 1's controller configuration. | ||
| 718 | if (player_index == 0 && player.controller_type == Settings::ControllerType::Handheld) { | ||
| 719 | const auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; | ||
| 720 | const auto handheld_connected = ui->groupConnectedController->isChecked() && | ||
| 721 | controller_type == Settings::ControllerType::Handheld; | ||
| 722 | // Disconnect only if handheld is going from connected to disconnected | ||
| 723 | if (handheld.connected && !handheld_connected) { | ||
| 724 | UpdateController(controller_type, HANDHELD_INDEX, false, system); | ||
| 725 | } | ||
| 726 | return; | ||
| 727 | } | ||
| 728 | |||
| 729 | // Do not do anything if the controller configuration has not changed. | ||
| 730 | if (player.controller_type == controller_type && player.connected == player_connected) { | ||
| 731 | return; | ||
| 732 | } | ||
| 733 | |||
| 734 | // Do not disconnect if the controller is already disconnected | ||
| 735 | if (!player.connected) { | ||
| 736 | return; | ||
| 737 | } | ||
| 738 | |||
| 739 | // Disconnect the controller first. | ||
| 740 | UpdateController(controller_type, player_index, false, system); | ||
| 741 | } | 679 | } |
| 742 | 680 | ||
| 743 | void ConfigureInputPlayer::showEvent(QShowEvent* event) { | 681 | void ConfigureInputPlayer::showEvent(QShowEvent* event) { |
| @@ -762,22 +700,7 @@ void ConfigureInputPlayer::RetranslateUI() { | |||
| 762 | } | 700 | } |
| 763 | 701 | ||
| 764 | void ConfigureInputPlayer::LoadConfiguration() { | 702 | void ConfigureInputPlayer::LoadConfiguration() { |
| 765 | auto& player = Settings::values.players.GetValue()[player_index]; | 703 | emulated_controller->ReloadFromSettings(); |
| 766 | if (debug) { | ||
| 767 | std::transform(Settings::values.debug_pad_buttons.begin(), | ||
| 768 | Settings::values.debug_pad_buttons.end(), buttons_param.begin(), | ||
| 769 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 770 | std::transform(Settings::values.debug_pad_analogs.begin(), | ||
| 771 | Settings::values.debug_pad_analogs.end(), analogs_param.begin(), | ||
| 772 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 773 | } else { | ||
| 774 | std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(), | ||
| 775 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 776 | std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(), | ||
| 777 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 778 | std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(), | ||
| 779 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 780 | } | ||
| 781 | 704 | ||
| 782 | UpdateUI(); | 705 | UpdateUI(); |
| 783 | UpdateInputDeviceCombobox(); | 706 | UpdateInputDeviceCombobox(); |
| @@ -786,14 +709,19 @@ void ConfigureInputPlayer::LoadConfiguration() { | |||
| 786 | return; | 709 | return; |
| 787 | } | 710 | } |
| 788 | 711 | ||
| 789 | ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type)); | 712 | const int comboBoxIndex = |
| 790 | ui->groupConnectedController->setChecked( | 713 | GetIndexFromControllerType(emulated_controller->GetNpadStyleIndex(true)); |
| 791 | player.connected || | 714 | ui->comboControllerType->setCurrentIndex(comboBoxIndex); |
| 792 | (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); | 715 | ui->groupConnectedController->setChecked(emulated_controller->IsConnected(true)); |
| 793 | } | 716 | } |
| 794 | 717 | ||
| 795 | void ConfigureInputPlayer::ConnectPlayer(bool connected) { | 718 | void ConfigureInputPlayer::ConnectPlayer(bool connected) { |
| 796 | ui->groupConnectedController->setChecked(connected); | 719 | ui->groupConnectedController->setChecked(connected); |
| 720 | if (connected) { | ||
| 721 | emulated_controller->Connect(); | ||
| 722 | } else { | ||
| 723 | emulated_controller->Disconnect(); | ||
| 724 | } | ||
| 797 | } | 725 | } |
| 798 | 726 | ||
| 799 | void ConfigureInputPlayer::UpdateInputDeviceCombobox() { | 727 | void ConfigureInputPlayer::UpdateInputDeviceCombobox() { |
| @@ -803,48 +731,64 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { | |||
| 803 | return; | 731 | return; |
| 804 | } | 732 | } |
| 805 | 733 | ||
| 806 | // Find the first button that isn't empty. | 734 | const auto devices = |
| 807 | const auto button_param = | 735 | emulated_controller->GetMappedDevices(Core::HID::EmulatedDeviceIndex::AllDevices); |
| 808 | std::find_if(buttons_param.begin(), buttons_param.end(), | 736 | UpdateInputDevices(); |
| 809 | [](const Common::ParamPackage param) { return param.Has("engine"); }); | ||
| 810 | const bool buttons_empty = button_param == buttons_param.end(); | ||
| 811 | 737 | ||
| 812 | const auto current_engine = button_param->Get("engine", ""); | 738 | if (devices.empty()) { |
| 813 | const auto current_guid = button_param->Get("guid", ""); | 739 | return; |
| 814 | const auto current_port = button_param->Get("port", ""); | 740 | } |
| 815 | 741 | ||
| 816 | const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse"; | 742 | if (devices.size() > 2) { |
| 743 | ui->comboDevices->setCurrentIndex(0); | ||
| 744 | return; | ||
| 745 | } | ||
| 817 | 746 | ||
| 818 | UpdateInputDevices(); | 747 | const auto first_engine = devices[0].Get("engine", ""); |
| 748 | const auto first_guid = devices[0].Get("guid", ""); | ||
| 749 | const auto first_port = devices[0].Get("port", 0); | ||
| 819 | 750 | ||
| 820 | if (buttons_empty) { | 751 | if (devices.size() == 1) { |
| 752 | const auto devices_it = | ||
| 753 | std::find_if(input_devices.begin(), input_devices.end(), | ||
| 754 | [first_engine, first_guid, first_port](const Common::ParamPackage param) { | ||
| 755 | return param.Get("engine", "") == first_engine && | ||
| 756 | param.Get("guid", "") == first_guid && | ||
| 757 | param.Get("port", 0) == first_port; | ||
| 758 | }); | ||
| 759 | const int device_index = | ||
| 760 | devices_it != input_devices.end() | ||
| 761 | ? static_cast<int>(std::distance(input_devices.begin(), devices_it)) | ||
| 762 | : 0; | ||
| 763 | ui->comboDevices->setCurrentIndex(device_index); | ||
| 821 | return; | 764 | return; |
| 822 | } | 765 | } |
| 823 | 766 | ||
| 824 | const bool all_one_device = | 767 | const auto second_engine = devices[1].Get("engine", ""); |
| 825 | std::all_of(buttons_param.begin(), buttons_param.end(), | 768 | const auto second_guid = devices[1].Get("guid", ""); |
| 826 | [current_engine, current_guid, current_port, | 769 | const auto second_port = devices[1].Get("port", 0); |
| 827 | is_keyboard_mouse](const Common::ParamPackage param) { | ||
| 828 | if (is_keyboard_mouse) { | ||
| 829 | return !param.Has("engine") || param.Get("engine", "") == "keyboard" || | ||
| 830 | param.Get("engine", "") == "mouse"; | ||
| 831 | } | ||
| 832 | return !param.Has("engine") || (param.Get("engine", "") == current_engine && | ||
| 833 | param.Get("guid", "") == current_guid && | ||
| 834 | param.Get("port", "") == current_port); | ||
| 835 | }); | ||
| 836 | 770 | ||
| 837 | if (all_one_device) { | 771 | const bool is_keyboard_mouse = (first_engine == "keyboard" || first_engine == "mouse") && |
| 838 | if (is_keyboard_mouse) { | 772 | (second_engine == "keyboard" || second_engine == "mouse"); |
| 839 | ui->comboDevices->setCurrentIndex(1); | 773 | |
| 840 | return; | 774 | if (is_keyboard_mouse) { |
| 841 | } | 775 | ui->comboDevices->setCurrentIndex(2); |
| 776 | return; | ||
| 777 | } | ||
| 778 | |||
| 779 | const bool is_engine_equal = first_engine == second_engine; | ||
| 780 | const bool is_port_equal = first_port == second_port; | ||
| 781 | |||
| 782 | if (is_engine_equal && is_port_equal) { | ||
| 842 | const auto devices_it = std::find_if( | 783 | const auto devices_it = std::find_if( |
| 843 | input_devices.begin(), input_devices.end(), | 784 | input_devices.begin(), input_devices.end(), |
| 844 | [current_engine, current_guid, current_port](const Common::ParamPackage param) { | 785 | [first_engine, first_guid, second_guid, first_port](const Common::ParamPackage param) { |
| 845 | return param.Get("class", "") == current_engine && | 786 | const bool is_guid_valid = |
| 846 | param.Get("guid", "") == current_guid && | 787 | (param.Get("guid", "") == first_guid && |
| 847 | param.Get("port", "") == current_port; | 788 | param.Get("guid2", "") == second_guid) || |
| 789 | (param.Get("guid", "") == second_guid && param.Get("guid2", "") == first_guid); | ||
| 790 | return param.Get("engine", "") == first_engine && is_guid_valid && | ||
| 791 | param.Get("port", 0) == first_port; | ||
| 848 | }); | 792 | }); |
| 849 | const int device_index = | 793 | const int device_index = |
| 850 | devices_it != input_devices.end() | 794 | devices_it != input_devices.end() |
| @@ -866,8 +810,7 @@ void ConfigureInputPlayer::ClearAll() { | |||
| 866 | if (button == nullptr) { | 810 | if (button == nullptr) { |
| 867 | continue; | 811 | continue; |
| 868 | } | 812 | } |
| 869 | 813 | emulated_controller->SetButtonParam(button_id, {}); | |
| 870 | buttons_param[button_id].Clear(); | ||
| 871 | } | 814 | } |
| 872 | 815 | ||
| 873 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { | 816 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { |
| @@ -876,8 +819,7 @@ void ConfigureInputPlayer::ClearAll() { | |||
| 876 | if (analog_button == nullptr) { | 819 | if (analog_button == nullptr) { |
| 877 | continue; | 820 | continue; |
| 878 | } | 821 | } |
| 879 | 822 | emulated_controller->SetStickParam(analog_id, {}); | |
| 880 | analogs_param[analog_id].Clear(); | ||
| 881 | } | 823 | } |
| 882 | } | 824 | } |
| 883 | 825 | ||
| @@ -886,8 +828,7 @@ void ConfigureInputPlayer::ClearAll() { | |||
| 886 | if (motion_button == nullptr) { | 828 | if (motion_button == nullptr) { |
| 887 | continue; | 829 | continue; |
| 888 | } | 830 | } |
| 889 | 831 | emulated_controller->SetMotionParam(motion_id, {}); | |
| 890 | motions_param[motion_id].Clear(); | ||
| 891 | } | 832 | } |
| 892 | 833 | ||
| 893 | UpdateUI(); | 834 | UpdateUI(); |
| @@ -896,26 +837,31 @@ void ConfigureInputPlayer::ClearAll() { | |||
| 896 | 837 | ||
| 897 | void ConfigureInputPlayer::UpdateUI() { | 838 | void ConfigureInputPlayer::UpdateUI() { |
| 898 | for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) { | 839 | for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) { |
| 899 | button_map[button]->setText(ButtonToText(buttons_param[button])); | 840 | const Common::ParamPackage param = emulated_controller->GetButtonParam(button); |
| 841 | button_map[button]->setText(ButtonToText(param)); | ||
| 900 | } | 842 | } |
| 901 | 843 | ||
| 902 | if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) { | 844 | const Common::ParamPackage ZL_param = |
| 903 | const int button_threshold = static_cast<int>( | 845 | emulated_controller->GetButtonParam(Settings::NativeButton::ZL); |
| 904 | buttons_param[Settings::NativeButton::ZL].Get("threshold", 0.5f) * 100.0f); | 846 | if (ZL_param.Has("threshold")) { |
| 847 | const int button_threshold = static_cast<int>(ZL_param.Get("threshold", 0.5f) * 100.0f); | ||
| 905 | ui->sliderZLThreshold->setValue(button_threshold); | 848 | ui->sliderZLThreshold->setValue(button_threshold); |
| 906 | } | 849 | } |
| 907 | 850 | ||
| 908 | if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) { | 851 | const Common::ParamPackage ZR_param = |
| 909 | const int button_threshold = static_cast<int>( | 852 | emulated_controller->GetButtonParam(Settings::NativeButton::ZR); |
| 910 | buttons_param[Settings::NativeButton::ZR].Get("threshold", 0.5f) * 100.0f); | 853 | if (ZR_param.Has("threshold")) { |
| 854 | const int button_threshold = static_cast<int>(ZR_param.Get("threshold", 0.5f) * 100.0f); | ||
| 911 | ui->sliderZRThreshold->setValue(button_threshold); | 855 | ui->sliderZRThreshold->setValue(button_threshold); |
| 912 | } | 856 | } |
| 913 | 857 | ||
| 914 | for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { | 858 | for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { |
| 915 | motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id])); | 859 | const Common::ParamPackage param = emulated_controller->GetMotionParam(motion_id); |
| 860 | motion_map[motion_id]->setText(ButtonToText(param)); | ||
| 916 | } | 861 | } |
| 917 | 862 | ||
| 918 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { | 863 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { |
| 864 | const Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); | ||
| 919 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { | 865 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { |
| 920 | auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; | 866 | auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; |
| 921 | 867 | ||
| @@ -923,12 +869,11 @@ void ConfigureInputPlayer::UpdateUI() { | |||
| 923 | continue; | 869 | continue; |
| 924 | } | 870 | } |
| 925 | 871 | ||
| 926 | analog_button->setText( | 872 | analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id])); |
| 927 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | ||
| 928 | } | 873 | } |
| 929 | 874 | ||
| 930 | analog_map_modifier_button[analog_id]->setText( | 875 | analog_map_modifier_button[analog_id]->setText( |
| 931 | ButtonToText(Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")})); | 876 | ButtonToText(Common::ParamPackage{param.Get("modifier", "")})); |
| 932 | 877 | ||
| 933 | const auto deadzone_label = analog_map_deadzone_label[analog_id]; | 878 | const auto deadzone_label = analog_map_deadzone_label[analog_id]; |
| 934 | const auto deadzone_slider = analog_map_deadzone_slider[analog_id]; | 879 | const auto deadzone_slider = analog_map_deadzone_slider[analog_id]; |
| @@ -939,26 +884,14 @@ void ConfigureInputPlayer::UpdateUI() { | |||
| 939 | const auto range_spinbox = analog_map_range_spinbox[analog_id]; | 884 | const auto range_spinbox = analog_map_range_spinbox[analog_id]; |
| 940 | 885 | ||
| 941 | int slider_value; | 886 | int slider_value; |
| 942 | auto& param = analogs_param[analog_id]; | 887 | const bool is_controller = input_subsystem->IsController(param); |
| 943 | const bool is_controller = | ||
| 944 | param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad" || | ||
| 945 | param.Get("engine", "") == "mouse" || param.Get("engine", "") == "tas"; | ||
| 946 | 888 | ||
| 947 | if (is_controller) { | 889 | if (is_controller) { |
| 948 | if (!param.Has("deadzone")) { | 890 | slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100); |
| 949 | param.Set("deadzone", 0.1f); | ||
| 950 | } | ||
| 951 | slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100); | ||
| 952 | deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value)); | 891 | deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value)); |
| 953 | deadzone_slider->setValue(slider_value); | 892 | deadzone_slider->setValue(slider_value); |
| 954 | if (!param.Has("range")) { | ||
| 955 | param.Set("range", 1.0f); | ||
| 956 | } | ||
| 957 | range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100)); | 893 | range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100)); |
| 958 | } else { | 894 | } else { |
| 959 | if (!param.Has("modifier_scale")) { | ||
| 960 | param.Set("modifier_scale", 0.5f); | ||
| 961 | } | ||
| 962 | slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100); | 895 | slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100); |
| 963 | modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value)); | 896 | modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value)); |
| 964 | modifier_slider->setValue(slider_value); | 897 | modifier_slider->setValue(slider_value); |
| @@ -970,79 +903,98 @@ void ConfigureInputPlayer::UpdateUI() { | |||
| 970 | modifier_label->setVisible(!is_controller); | 903 | modifier_label->setVisible(!is_controller); |
| 971 | modifier_slider->setVisible(!is_controller); | 904 | modifier_slider->setVisible(!is_controller); |
| 972 | range_groupbox->setVisible(is_controller); | 905 | range_groupbox->setVisible(is_controller); |
| 973 | ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); | ||
| 974 | } | 906 | } |
| 975 | } | 907 | } |
| 976 | 908 | ||
| 977 | void ConfigureInputPlayer::SetConnectableControllers() { | 909 | void ConfigureInputPlayer::SetConnectableControllers() { |
| 978 | const auto add_controllers = [this](bool enable_all, | 910 | Core::HID::NpadStyleTag npad_style_set = hid_core.GetSupportedStyleTag(); |
| 979 | Controller_NPad::NpadStyleSet npad_style_set = {}) { | 911 | index_controller_type_pairs.clear(); |
| 980 | index_controller_type_pairs.clear(); | 912 | ui->comboControllerType->clear(); |
| 981 | ui->comboControllerType->clear(); | ||
| 982 | |||
| 983 | if (enable_all || npad_style_set.fullkey == 1) { | ||
| 984 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | ||
| 985 | Settings::ControllerType::ProController); | ||
| 986 | ui->comboControllerType->addItem(tr("Pro Controller")); | ||
| 987 | } | ||
| 988 | 913 | ||
| 989 | if (enable_all || npad_style_set.joycon_dual == 1) { | 914 | if (npad_style_set.fullkey == 1) { |
| 990 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | 915 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), |
| 991 | Settings::ControllerType::DualJoyconDetached); | 916 | Core::HID::NpadStyleIndex::ProController); |
| 992 | ui->comboControllerType->addItem(tr("Dual Joycons")); | 917 | ui->comboControllerType->addItem(tr("Pro Controller")); |
| 993 | } | 918 | } |
| 994 | 919 | ||
| 995 | if (enable_all || npad_style_set.joycon_left == 1) { | 920 | if (npad_style_set.joycon_dual == 1) { |
| 996 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | 921 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), |
| 997 | Settings::ControllerType::LeftJoycon); | 922 | Core::HID::NpadStyleIndex::JoyconDual); |
| 998 | ui->comboControllerType->addItem(tr("Left Joycon")); | 923 | ui->comboControllerType->addItem(tr("Dual Joycons")); |
| 999 | } | 924 | } |
| 1000 | 925 | ||
| 1001 | if (enable_all || npad_style_set.joycon_right == 1) { | 926 | if (npad_style_set.joycon_left == 1) { |
| 1002 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | 927 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), |
| 1003 | Settings::ControllerType::RightJoycon); | 928 | Core::HID::NpadStyleIndex::JoyconLeft); |
| 1004 | ui->comboControllerType->addItem(tr("Right Joycon")); | 929 | ui->comboControllerType->addItem(tr("Left Joycon")); |
| 1005 | } | 930 | } |
| 1006 | 931 | ||
| 1007 | if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) { | 932 | if (npad_style_set.joycon_right == 1) { |
| 1008 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | 933 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), |
| 1009 | Settings::ControllerType::Handheld); | 934 | Core::HID::NpadStyleIndex::JoyconRight); |
| 1010 | ui->comboControllerType->addItem(tr("Handheld")); | 935 | ui->comboControllerType->addItem(tr("Right Joycon")); |
| 1011 | } | 936 | } |
| 1012 | 937 | ||
| 1013 | if (enable_all || npad_style_set.gamecube == 1) { | 938 | if (player_index == 0 && npad_style_set.handheld == 1) { |
| 1014 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | 939 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), |
| 1015 | Settings::ControllerType::GameCube); | 940 | Core::HID::NpadStyleIndex::Handheld); |
| 1016 | ui->comboControllerType->addItem(tr("GameCube Controller")); | 941 | ui->comboControllerType->addItem(tr("Handheld")); |
| 1017 | } | 942 | } |
| 1018 | }; | 943 | |
| 944 | if (npad_style_set.gamecube == 1) { | ||
| 945 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | ||
| 946 | Core::HID::NpadStyleIndex::GameCube); | ||
| 947 | ui->comboControllerType->addItem(tr("GameCube Controller")); | ||
| 948 | } | ||
| 1019 | 949 | ||
| 1020 | if (!system.IsPoweredOn()) { | 950 | // Disable all unsupported controllers |
| 1021 | add_controllers(true); | 951 | if (!Settings::values.enable_all_controllers) { |
| 1022 | return; | 952 | return; |
| 1023 | } | 953 | } |
| 954 | if (npad_style_set.palma == 1) { | ||
| 955 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | ||
| 956 | Core::HID::NpadStyleIndex::Pokeball); | ||
| 957 | ui->comboControllerType->addItem(tr("Poke Ball Plus")); | ||
| 958 | } | ||
| 1024 | 959 | ||
| 1025 | Service::SM::ServiceManager& sm = system.ServiceManager(); | 960 | if (npad_style_set.lark == 1) { |
| 961 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | ||
| 962 | Core::HID::NpadStyleIndex::NES); | ||
| 963 | ui->comboControllerType->addItem(tr("NES Controller")); | ||
| 964 | } | ||
| 1026 | 965 | ||
| 1027 | auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>( | 966 | if (npad_style_set.lucia == 1) { |
| 1028 | HidController::NPad); | 967 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), |
| 968 | Core::HID::NpadStyleIndex::SNES); | ||
| 969 | ui->comboControllerType->addItem(tr("SNES Controller")); | ||
| 970 | } | ||
| 1029 | 971 | ||
| 1030 | add_controllers(false, npad.GetSupportedStyleSet()); | 972 | if (npad_style_set.lagoon == 1) { |
| 973 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | ||
| 974 | Core::HID::NpadStyleIndex::N64); | ||
| 975 | ui->comboControllerType->addItem(tr("N64 Controller")); | ||
| 976 | } | ||
| 977 | |||
| 978 | if (npad_style_set.lager == 1) { | ||
| 979 | index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), | ||
| 980 | Core::HID::NpadStyleIndex::SegaGenesis); | ||
| 981 | ui->comboControllerType->addItem(tr("Sega Genesis")); | ||
| 982 | } | ||
| 1031 | } | 983 | } |
| 1032 | 984 | ||
| 1033 | Settings::ControllerType ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const { | 985 | Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const { |
| 1034 | const auto it = | 986 | const auto it = |
| 1035 | std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), | 987 | std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), |
| 1036 | [index](const auto& pair) { return pair.first == index; }); | 988 | [index](const auto& pair) { return pair.first == index; }); |
| 1037 | 989 | ||
| 1038 | if (it == index_controller_type_pairs.end()) { | 990 | if (it == index_controller_type_pairs.end()) { |
| 1039 | return Settings::ControllerType::ProController; | 991 | return Core::HID::NpadStyleIndex::ProController; |
| 1040 | } | 992 | } |
| 1041 | 993 | ||
| 1042 | return it->second; | 994 | return it->second; |
| 1043 | } | 995 | } |
| 1044 | 996 | ||
| 1045 | int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType type) const { | 997 | int ConfigureInputPlayer::GetIndexFromControllerType(Core::HID::NpadStyleIndex type) const { |
| 1046 | const auto it = | 998 | const auto it = |
| 1047 | std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), | 999 | std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), |
| 1048 | [type](const auto& pair) { return pair.second == type; }); | 1000 | [type](const auto& pair) { return pair.second == type; }); |
| @@ -1057,52 +1009,15 @@ int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType ty | |||
| 1057 | void ConfigureInputPlayer::UpdateInputDevices() { | 1009 | void ConfigureInputPlayer::UpdateInputDevices() { |
| 1058 | input_devices = input_subsystem->GetInputDevices(); | 1010 | input_devices = input_subsystem->GetInputDevices(); |
| 1059 | ui->comboDevices->clear(); | 1011 | ui->comboDevices->clear(); |
| 1060 | for (auto& device : input_devices) { | 1012 | for (auto device : input_devices) { |
| 1061 | const std::string display = device.Get("display", "Unknown"); | 1013 | ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); |
| 1062 | ui->comboDevices->addItem(QString::fromStdString(display), {}); | ||
| 1063 | if (display == "TAS") { | ||
| 1064 | device.Set("pad", static_cast<u8>(player_index)); | ||
| 1065 | } | ||
| 1066 | } | 1014 | } |
| 1067 | } | 1015 | } |
| 1068 | 1016 | ||
| 1069 | void ConfigureInputPlayer::UpdateControllerIcon() { | ||
| 1070 | // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its | ||
| 1071 | // "nonstandard" to use an image through the icon support) | ||
| 1072 | const QString stylesheet = [this] { | ||
| 1073 | switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { | ||
| 1074 | case Settings::ControllerType::ProController: | ||
| 1075 | return QStringLiteral("image: url(:/controller/pro_controller%0)"); | ||
| 1076 | case Settings::ControllerType::DualJoyconDetached: | ||
| 1077 | return QStringLiteral("image: url(:/controller/dual_joycon%0)"); | ||
| 1078 | case Settings::ControllerType::LeftJoycon: | ||
| 1079 | return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)"); | ||
| 1080 | case Settings::ControllerType::RightJoycon: | ||
| 1081 | return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)"); | ||
| 1082 | case Settings::ControllerType::Handheld: | ||
| 1083 | return QStringLiteral("image: url(:/controller/handheld%0)"); | ||
| 1084 | default: | ||
| 1085 | return QString{}; | ||
| 1086 | } | ||
| 1087 | }(); | ||
| 1088 | |||
| 1089 | const QString theme = [] { | ||
| 1090 | if (QIcon::themeName().contains(QStringLiteral("dark"))) { | ||
| 1091 | return QStringLiteral("_dark"); | ||
| 1092 | } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { | ||
| 1093 | return QStringLiteral("_midnight"); | ||
| 1094 | } else { | ||
| 1095 | return QString{}; | ||
| 1096 | } | ||
| 1097 | }(); | ||
| 1098 | ui->controllerFrame->SetControllerType( | ||
| 1099 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())); | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | 1017 | void ConfigureInputPlayer::UpdateControllerAvailableButtons() { |
| 1103 | auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | 1018 | auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); |
| 1104 | if (debug) { | 1019 | if (debug) { |
| 1105 | layout = Settings::ControllerType::ProController; | 1020 | layout = Core::HID::NpadStyleIndex::ProController; |
| 1106 | } | 1021 | } |
| 1107 | 1022 | ||
| 1108 | // List of all the widgets that will be hidden by any of the following layouts that need | 1023 | // List of all the widgets that will be hidden by any of the following layouts that need |
| @@ -1127,15 +1042,15 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | |||
| 1127 | 1042 | ||
| 1128 | std::vector<QWidget*> layout_hidden; | 1043 | std::vector<QWidget*> layout_hidden; |
| 1129 | switch (layout) { | 1044 | switch (layout) { |
| 1130 | case Settings::ControllerType::ProController: | 1045 | case Core::HID::NpadStyleIndex::ProController: |
| 1131 | case Settings::ControllerType::DualJoyconDetached: | 1046 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 1132 | case Settings::ControllerType::Handheld: | 1047 | case Core::HID::NpadStyleIndex::Handheld: |
| 1133 | layout_hidden = { | 1048 | layout_hidden = { |
| 1134 | ui->buttonShoulderButtonsSLSR, | 1049 | ui->buttonShoulderButtonsSLSR, |
| 1135 | ui->horizontalSpacerShoulderButtonsWidget2, | 1050 | ui->horizontalSpacerShoulderButtonsWidget2, |
| 1136 | }; | 1051 | }; |
| 1137 | break; | 1052 | break; |
| 1138 | case Settings::ControllerType::LeftJoycon: | 1053 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1139 | layout_hidden = { | 1054 | layout_hidden = { |
| 1140 | ui->horizontalSpacerShoulderButtonsWidget2, | 1055 | ui->horizontalSpacerShoulderButtonsWidget2, |
| 1141 | ui->buttonShoulderButtonsRight, | 1056 | ui->buttonShoulderButtonsRight, |
| @@ -1143,7 +1058,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | |||
| 1143 | ui->bottomRight, | 1058 | ui->bottomRight, |
| 1144 | }; | 1059 | }; |
| 1145 | break; | 1060 | break; |
| 1146 | case Settings::ControllerType::RightJoycon: | 1061 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1147 | layout_hidden = { | 1062 | layout_hidden = { |
| 1148 | ui->horizontalSpacerShoulderButtonsWidget, | 1063 | ui->horizontalSpacerShoulderButtonsWidget, |
| 1149 | ui->buttonShoulderButtonsLeft, | 1064 | ui->buttonShoulderButtonsLeft, |
| @@ -1151,7 +1066,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | |||
| 1151 | ui->bottomLeft, | 1066 | ui->bottomLeft, |
| 1152 | }; | 1067 | }; |
| 1153 | break; | 1068 | break; |
| 1154 | case Settings::ControllerType::GameCube: | 1069 | case Core::HID::NpadStyleIndex::GameCube: |
| 1155 | layout_hidden = { | 1070 | layout_hidden = { |
| 1156 | ui->buttonShoulderButtonsSLSR, | 1071 | ui->buttonShoulderButtonsSLSR, |
| 1157 | ui->horizontalSpacerShoulderButtonsWidget2, | 1072 | ui->horizontalSpacerShoulderButtonsWidget2, |
| @@ -1159,6 +1074,8 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | |||
| 1159 | ui->buttonMiscButtonsScreenshotGroup, | 1074 | ui->buttonMiscButtonsScreenshotGroup, |
| 1160 | }; | 1075 | }; |
| 1161 | break; | 1076 | break; |
| 1077 | default: | ||
| 1078 | break; | ||
| 1162 | } | 1079 | } |
| 1163 | 1080 | ||
| 1164 | for (auto* widget : layout_hidden) { | 1081 | for (auto* widget : layout_hidden) { |
| @@ -1169,13 +1086,12 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | |||
| 1169 | void ConfigureInputPlayer::UpdateControllerEnabledButtons() { | 1086 | void ConfigureInputPlayer::UpdateControllerEnabledButtons() { |
| 1170 | auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | 1087 | auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); |
| 1171 | if (debug) { | 1088 | if (debug) { |
| 1172 | layout = Settings::ControllerType::ProController; | 1089 | layout = Core::HID::NpadStyleIndex::ProController; |
| 1173 | } | 1090 | } |
| 1174 | 1091 | ||
| 1175 | // List of all the widgets that will be disabled by any of the following layouts that need | 1092 | // List of all the widgets that will be disabled by any of the following layouts that need |
| 1176 | // "enabled" after the controller type changes | 1093 | // "enabled" after the controller type changes |
| 1177 | const std::array<QWidget*, 4> layout_enable = { | 1094 | const std::array<QWidget*, 3> layout_enable = { |
| 1178 | ui->buttonHome, | ||
| 1179 | ui->buttonLStickPressedGroup, | 1095 | ui->buttonLStickPressedGroup, |
| 1180 | ui->groupRStickPressed, | 1096 | ui->groupRStickPressed, |
| 1181 | ui->buttonShoulderButtonsButtonLGroup, | 1097 | ui->buttonShoulderButtonsButtonLGroup, |
| @@ -1187,17 +1103,13 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() { | |||
| 1187 | 1103 | ||
| 1188 | std::vector<QWidget*> layout_disable; | 1104 | std::vector<QWidget*> layout_disable; |
| 1189 | switch (layout) { | 1105 | switch (layout) { |
| 1190 | case Settings::ControllerType::ProController: | 1106 | case Core::HID::NpadStyleIndex::ProController: |
| 1191 | case Settings::ControllerType::DualJoyconDetached: | 1107 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 1192 | case Settings::ControllerType::Handheld: | 1108 | case Core::HID::NpadStyleIndex::Handheld: |
| 1193 | case Settings::ControllerType::LeftJoycon: | 1109 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1194 | case Settings::ControllerType::RightJoycon: | 1110 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1195 | // TODO(wwylele): enable this when we actually emulate it | ||
| 1196 | layout_disable = { | ||
| 1197 | ui->buttonHome, | ||
| 1198 | }; | ||
| 1199 | break; | 1111 | break; |
| 1200 | case Settings::ControllerType::GameCube: | 1112 | case Core::HID::NpadStyleIndex::GameCube: |
| 1201 | layout_disable = { | 1113 | layout_disable = { |
| 1202 | ui->buttonHome, | 1114 | ui->buttonHome, |
| 1203 | ui->buttonLStickPressedGroup, | 1115 | ui->buttonLStickPressedGroup, |
| @@ -1205,6 +1117,8 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() { | |||
| 1205 | ui->buttonShoulderButtonsButtonLGroup, | 1117 | ui->buttonShoulderButtonsButtonLGroup, |
| 1206 | }; | 1118 | }; |
| 1207 | break; | 1119 | break; |
| 1120 | default: | ||
| 1121 | break; | ||
| 1208 | } | 1122 | } |
| 1209 | 1123 | ||
| 1210 | for (auto* widget : layout_disable) { | 1124 | for (auto* widget : layout_disable) { |
| @@ -1222,24 +1136,24 @@ void ConfigureInputPlayer::UpdateMotionButtons() { | |||
| 1222 | 1136 | ||
| 1223 | // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. | 1137 | // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. |
| 1224 | switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { | 1138 | switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { |
| 1225 | case Settings::ControllerType::ProController: | 1139 | case Core::HID::NpadStyleIndex::ProController: |
| 1226 | case Settings::ControllerType::LeftJoycon: | 1140 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1227 | case Settings::ControllerType::Handheld: | 1141 | case Core::HID::NpadStyleIndex::Handheld: |
| 1228 | // Show "Motion 1" and hide "Motion 2". | 1142 | // Show "Motion 1" and hide "Motion 2". |
| 1229 | ui->buttonMotionLeftGroup->show(); | 1143 | ui->buttonMotionLeftGroup->show(); |
| 1230 | ui->buttonMotionRightGroup->hide(); | 1144 | ui->buttonMotionRightGroup->hide(); |
| 1231 | break; | 1145 | break; |
| 1232 | case Settings::ControllerType::RightJoycon: | 1146 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1233 | // Show "Motion 2" and hide "Motion 1". | 1147 | // Show "Motion 2" and hide "Motion 1". |
| 1234 | ui->buttonMotionLeftGroup->hide(); | 1148 | ui->buttonMotionLeftGroup->hide(); |
| 1235 | ui->buttonMotionRightGroup->show(); | 1149 | ui->buttonMotionRightGroup->show(); |
| 1236 | break; | 1150 | break; |
| 1237 | case Settings::ControllerType::GameCube: | 1151 | case Core::HID::NpadStyleIndex::GameCube: |
| 1238 | // Hide both "Motion 1/2". | 1152 | // Hide both "Motion 1/2". |
| 1239 | ui->buttonMotionLeftGroup->hide(); | 1153 | ui->buttonMotionLeftGroup->hide(); |
| 1240 | ui->buttonMotionRightGroup->hide(); | 1154 | ui->buttonMotionRightGroup->hide(); |
| 1241 | break; | 1155 | break; |
| 1242 | case Settings::ControllerType::DualJoyconDetached: | 1156 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 1243 | default: | 1157 | default: |
| 1244 | // Show both "Motion 1/2". | 1158 | // Show both "Motion 1/2". |
| 1245 | ui->buttonMotionLeftGroup->show(); | 1159 | ui->buttonMotionLeftGroup->show(); |
| @@ -1251,15 +1165,15 @@ void ConfigureInputPlayer::UpdateMotionButtons() { | |||
| 1251 | void ConfigureInputPlayer::UpdateControllerButtonNames() { | 1165 | void ConfigureInputPlayer::UpdateControllerButtonNames() { |
| 1252 | auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | 1166 | auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); |
| 1253 | if (debug) { | 1167 | if (debug) { |
| 1254 | layout = Settings::ControllerType::ProController; | 1168 | layout = Core::HID::NpadStyleIndex::ProController; |
| 1255 | } | 1169 | } |
| 1256 | 1170 | ||
| 1257 | switch (layout) { | 1171 | switch (layout) { |
| 1258 | case Settings::ControllerType::ProController: | 1172 | case Core::HID::NpadStyleIndex::ProController: |
| 1259 | case Settings::ControllerType::DualJoyconDetached: | 1173 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 1260 | case Settings::ControllerType::Handheld: | 1174 | case Core::HID::NpadStyleIndex::Handheld: |
| 1261 | case Settings::ControllerType::LeftJoycon: | 1175 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1262 | case Settings::ControllerType::RightJoycon: | 1176 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1263 | ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus")); | 1177 | ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus")); |
| 1264 | ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL")); | 1178 | ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL")); |
| 1265 | ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR")); | 1179 | ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR")); |
| @@ -1267,7 +1181,7 @@ void ConfigureInputPlayer::UpdateControllerButtonNames() { | |||
| 1267 | ui->LStick->setTitle(tr("Left Stick")); | 1181 | ui->LStick->setTitle(tr("Left Stick")); |
| 1268 | ui->RStick->setTitle(tr("Right Stick")); | 1182 | ui->RStick->setTitle(tr("Right Stick")); |
| 1269 | break; | 1183 | break; |
| 1270 | case Settings::ControllerType::GameCube: | 1184 | case Core::HID::NpadStyleIndex::GameCube: |
| 1271 | ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause")); | 1185 | ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause")); |
| 1272 | ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L")); | 1186 | ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L")); |
| 1273 | ui->buttonShoulderButtonsZRGroup->setTitle(tr("R")); | 1187 | ui->buttonShoulderButtonsZRGroup->setTitle(tr("R")); |
| @@ -1275,6 +1189,8 @@ void ConfigureInputPlayer::UpdateControllerButtonNames() { | |||
| 1275 | ui->LStick->setTitle(tr("Control Stick")); | 1189 | ui->LStick->setTitle(tr("Control Stick")); |
| 1276 | ui->RStick->setTitle(tr("C-Stick")); | 1190 | ui->RStick->setTitle(tr("C-Stick")); |
| 1277 | break; | 1191 | break; |
| 1192 | default: | ||
| 1193 | break; | ||
| 1278 | } | 1194 | } |
| 1279 | } | 1195 | } |
| 1280 | 1196 | ||
| @@ -1283,45 +1199,82 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() { | |||
| 1283 | return; | 1199 | return; |
| 1284 | } | 1200 | } |
| 1285 | 1201 | ||
| 1286 | if (ui->comboDevices->currentIndex() == 1) { | 1202 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { |
| 1287 | // Reset keyboard bindings | 1203 | const auto* const button = button_map[button_id]; |
| 1204 | if (button == nullptr) { | ||
| 1205 | continue; | ||
| 1206 | } | ||
| 1207 | emulated_controller->SetButtonParam(button_id, {}); | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { | ||
| 1211 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { | ||
| 1212 | const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; | ||
| 1213 | if (analog_button == nullptr) { | ||
| 1214 | continue; | ||
| 1215 | } | ||
| 1216 | emulated_controller->SetStickParam(analog_id, {}); | ||
| 1217 | } | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { | ||
| 1221 | const auto* const motion_button = motion_map[motion_id]; | ||
| 1222 | if (motion_button == nullptr) { | ||
| 1223 | continue; | ||
| 1224 | } | ||
| 1225 | emulated_controller->SetMotionParam(motion_id, {}); | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | // Reset keyboard or mouse bindings | ||
| 1229 | if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) { | ||
| 1288 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { | 1230 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { |
| 1289 | buttons_param[button_id] = Common::ParamPackage{ | 1231 | emulated_controller->SetButtonParam( |
| 1290 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; | 1232 | button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( |
| 1233 | Config::default_buttons[button_id])}); | ||
| 1291 | } | 1234 | } |
| 1292 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { | 1235 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { |
| 1236 | Common::ParamPackage analog_param{}; | ||
| 1293 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { | 1237 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { |
| 1294 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( | 1238 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( |
| 1295 | Config::default_analogs[analog_id][sub_button_id])}; | 1239 | Config::default_analogs[analog_id][sub_button_id])}; |
| 1296 | SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); | 1240 | SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); |
| 1297 | } | 1241 | } |
| 1298 | 1242 | ||
| 1299 | analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam( | 1243 | analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( |
| 1300 | Config::default_stick_mod[analog_id])); | 1244 | Config::default_stick_mod[analog_id])); |
| 1245 | emulated_controller->SetStickParam(analog_id, analog_param); | ||
| 1301 | } | 1246 | } |
| 1302 | 1247 | ||
| 1303 | for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { | 1248 | for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { |
| 1304 | motions_param[motion_id] = Common::ParamPackage{ | 1249 | emulated_controller->SetMotionParam( |
| 1305 | InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; | 1250 | motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( |
| 1251 | Config::default_motions[motion_id])}); | ||
| 1306 | } | 1252 | } |
| 1307 | 1253 | ||
| 1308 | UpdateUI(); | 1254 | // If mouse is selected we want to override with mappings from the driver |
| 1309 | return; | 1255 | if (ui->comboDevices->currentIndex() == 1) { |
| 1256 | UpdateUI(); | ||
| 1257 | return; | ||
| 1258 | } | ||
| 1310 | } | 1259 | } |
| 1311 | 1260 | ||
| 1312 | // Reset controller bindings | 1261 | // Reset controller bindings |
| 1313 | const auto& device = input_devices[ui->comboDevices->currentIndex()]; | 1262 | const auto& device = input_devices[ui->comboDevices->currentIndex()]; |
| 1314 | auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); | 1263 | auto button_mappings = input_subsystem->GetButtonMappingForDevice(device); |
| 1315 | auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); | 1264 | auto analog_mappings = input_subsystem->GetAnalogMappingForDevice(device); |
| 1316 | auto motion_mapping = input_subsystem->GetMotionMappingForDevice(device); | 1265 | auto motion_mappings = input_subsystem->GetMotionMappingForDevice(device); |
| 1317 | for (std::size_t i = 0; i < buttons_param.size(); ++i) { | 1266 | |
| 1318 | buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)]; | 1267 | for (const auto& button_mapping : button_mappings) { |
| 1268 | const std::size_t index = button_mapping.first; | ||
| 1269 | emulated_controller->SetButtonParam(index, button_mapping.second); | ||
| 1319 | } | 1270 | } |
| 1320 | for (std::size_t i = 0; i < analogs_param.size(); ++i) { | 1271 | for (const auto& analog_mapping : analog_mappings) { |
| 1321 | analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)]; | 1272 | const std::size_t index = analog_mapping.first; |
| 1273 | emulated_controller->SetStickParam(index, analog_mapping.second); | ||
| 1322 | } | 1274 | } |
| 1323 | for (std::size_t i = 0; i < motions_param.size(); ++i) { | 1275 | for (const auto& motion_mapping : motion_mappings) { |
| 1324 | motions_param[i] = motion_mapping[static_cast<Settings::NativeMotion::Values>(i)]; | 1276 | const std::size_t index = motion_mapping.first; |
| 1277 | emulated_controller->SetMotionParam(index, motion_mapping.second); | ||
| 1325 | } | 1278 | } |
| 1326 | 1279 | ||
| 1327 | UpdateUI(); | 1280 | UpdateUI(); |
| @@ -1330,7 +1283,7 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() { | |||
| 1330 | void ConfigureInputPlayer::HandleClick( | 1283 | void ConfigureInputPlayer::HandleClick( |
| 1331 | QPushButton* button, std::size_t button_id, | 1284 | QPushButton* button, std::size_t button_id, |
| 1332 | std::function<void(const Common::ParamPackage&)> new_input_setter, | 1285 | std::function<void(const Common::ParamPackage&)> new_input_setter, |
| 1333 | InputCommon::Polling::DeviceType type) { | 1286 | InputCommon::Polling::InputType type) { |
| 1334 | if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { | 1287 | if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { |
| 1335 | button->setText(tr("Shake!")); | 1288 | button->setText(tr("Shake!")); |
| 1336 | } else { | 1289 | } else { |
| @@ -1338,71 +1291,31 @@ void ConfigureInputPlayer::HandleClick( | |||
| 1338 | } | 1291 | } |
| 1339 | button->setFocus(); | 1292 | button->setFocus(); |
| 1340 | 1293 | ||
| 1341 | // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a | ||
| 1342 | // controller, then they don't want keyboard/mouse input | ||
| 1343 | want_keyboard_mouse = ui->comboDevices->currentIndex() < 2; | ||
| 1344 | |||
| 1345 | input_setter = new_input_setter; | 1294 | input_setter = new_input_setter; |
| 1346 | 1295 | ||
| 1347 | device_pollers = input_subsystem->GetPollers(type); | 1296 | input_subsystem->BeginMapping(type); |
| 1348 | |||
| 1349 | for (auto& poller : device_pollers) { | ||
| 1350 | poller->Start(); | ||
| 1351 | } | ||
| 1352 | 1297 | ||
| 1353 | QWidget::grabMouse(); | 1298 | QWidget::grabMouse(); |
| 1354 | QWidget::grabKeyboard(); | 1299 | QWidget::grabKeyboard(); |
| 1355 | 1300 | ||
| 1356 | if (type == InputCommon::Polling::DeviceType::Button) { | 1301 | if (type == InputCommon::Polling::InputType::Button) { |
| 1357 | input_subsystem->GetGCButtons()->BeginConfiguration(); | ||
| 1358 | } else { | ||
| 1359 | input_subsystem->GetGCAnalogs()->BeginConfiguration(); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | if (type == InputCommon::Polling::DeviceType::Motion) { | ||
| 1363 | input_subsystem->GetUDPMotions()->BeginConfiguration(); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | if (type == InputCommon::Polling::DeviceType::Button) { | ||
| 1367 | input_subsystem->GetMouseButtons()->BeginConfiguration(); | ||
| 1368 | } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) { | ||
| 1369 | input_subsystem->GetMouseAnalogs()->BeginConfiguration(); | ||
| 1370 | } else if (type == InputCommon::Polling::DeviceType::Motion) { | ||
| 1371 | input_subsystem->GetMouseMotions()->BeginConfiguration(); | ||
| 1372 | } else { | ||
| 1373 | input_subsystem->GetMouseTouch()->BeginConfiguration(); | ||
| 1374 | } | ||
| 1375 | |||
| 1376 | if (type == InputCommon::Polling::DeviceType::Button) { | ||
| 1377 | ui->controllerFrame->BeginMappingButton(button_id); | 1302 | ui->controllerFrame->BeginMappingButton(button_id); |
| 1378 | } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) { | 1303 | } else if (type == InputCommon::Polling::InputType::Stick) { |
| 1379 | ui->controllerFrame->BeginMappingAnalog(button_id); | 1304 | ui->controllerFrame->BeginMappingAnalog(button_id); |
| 1380 | } | 1305 | } |
| 1381 | 1306 | ||
| 1382 | timeout_timer->start(2500); // Cancel after 2.5 seconds | 1307 | timeout_timer->start(2500); // Cancel after 2.5 seconds |
| 1383 | poll_timer->start(50); // Check for new inputs every 50ms | 1308 | poll_timer->start(25); // Check for new inputs every 25ms |
| 1384 | } | 1309 | } |
| 1385 | 1310 | ||
| 1386 | void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { | 1311 | void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { |
| 1387 | timeout_timer->stop(); | 1312 | timeout_timer->stop(); |
| 1388 | poll_timer->stop(); | 1313 | poll_timer->stop(); |
| 1389 | for (auto& poller : device_pollers) { | 1314 | input_subsystem->StopMapping(); |
| 1390 | poller->Stop(); | ||
| 1391 | } | ||
| 1392 | 1315 | ||
| 1393 | QWidget::releaseMouse(); | 1316 | QWidget::releaseMouse(); |
| 1394 | QWidget::releaseKeyboard(); | 1317 | QWidget::releaseKeyboard(); |
| 1395 | 1318 | ||
| 1396 | input_subsystem->GetGCButtons()->EndConfiguration(); | ||
| 1397 | input_subsystem->GetGCAnalogs()->EndConfiguration(); | ||
| 1398 | |||
| 1399 | input_subsystem->GetUDPMotions()->EndConfiguration(); | ||
| 1400 | |||
| 1401 | input_subsystem->GetMouseButtons()->EndConfiguration(); | ||
| 1402 | input_subsystem->GetMouseAnalogs()->EndConfiguration(); | ||
| 1403 | input_subsystem->GetMouseMotions()->EndConfiguration(); | ||
| 1404 | input_subsystem->GetMouseTouch()->EndConfiguration(); | ||
| 1405 | |||
| 1406 | if (!abort) { | 1319 | if (!abort) { |
| 1407 | (*input_setter)(params); | 1320 | (*input_setter)(params); |
| 1408 | } | 1321 | } |
| @@ -1419,15 +1332,20 @@ bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) | |||
| 1419 | return true; | 1332 | return true; |
| 1420 | } | 1333 | } |
| 1421 | 1334 | ||
| 1335 | if (params.Has("motion")) { | ||
| 1336 | return true; | ||
| 1337 | } | ||
| 1338 | |||
| 1422 | // Keyboard/Mouse | 1339 | // Keyboard/Mouse |
| 1423 | if (ui->comboDevices->currentIndex() == 1) { | 1340 | if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) { |
| 1424 | return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse"; | 1341 | return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse"; |
| 1425 | } | 1342 | } |
| 1426 | 1343 | ||
| 1427 | const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; | 1344 | const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; |
| 1428 | return params.Get("engine", "") == current_input_device.Get("class", "") && | 1345 | return params.Get("engine", "") == current_input_device.Get("engine", "") && |
| 1429 | params.Get("guid", "") == current_input_device.Get("guid", "") && | 1346 | (params.Get("guid", "") == current_input_device.Get("guid", "") || |
| 1430 | params.Get("port", "") == current_input_device.Get("port", ""); | 1347 | params.Get("guid", "") == current_input_device.Get("guid2", "")) && |
| 1348 | params.Get("port", 0) == current_input_device.Get("port", 0); | ||
| 1431 | } | 1349 | } |
| 1432 | 1350 | ||
| 1433 | void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { | 1351 | void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { |
| @@ -1436,25 +1354,17 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { | |||
| 1436 | } | 1354 | } |
| 1437 | 1355 | ||
| 1438 | const auto button = GRenderWindow::QtButtonToMouseButton(event->button()); | 1356 | const auto button = GRenderWindow::QtButtonToMouseButton(event->button()); |
| 1439 | input_subsystem->GetMouse()->PressButton(0, 0, button); | 1357 | input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button); |
| 1440 | } | 1358 | } |
| 1441 | 1359 | ||
| 1442 | void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { | 1360 | void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { |
| 1361 | event->ignore(); | ||
| 1443 | if (!input_setter || !event) { | 1362 | if (!input_setter || !event) { |
| 1444 | return; | 1363 | return; |
| 1445 | } | 1364 | } |
| 1446 | |||
| 1447 | if (event->key() != Qt::Key_Escape) { | 1365 | if (event->key() != Qt::Key_Escape) { |
| 1448 | if (want_keyboard_mouse) { | 1366 | input_subsystem->GetKeyboard()->PressKey(event->key()); |
| 1449 | SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, | ||
| 1450 | false); | ||
| 1451 | } else { | ||
| 1452 | // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling | ||
| 1453 | return; | ||
| 1454 | } | ||
| 1455 | } | 1367 | } |
| 1456 | |||
| 1457 | SetPollingResult({}, true); | ||
| 1458 | } | 1368 | } |
| 1459 | 1369 | ||
| 1460 | void ConfigureInputPlayer::CreateProfile() { | 1370 | void ConfigureInputPlayer::CreateProfile() { |
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 39b44b8a5..47df6b3d3 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h | |||
| @@ -29,48 +29,37 @@ class QWidget; | |||
| 29 | 29 | ||
| 30 | class InputProfiles; | 30 | class InputProfiles; |
| 31 | 31 | ||
| 32 | namespace Core { | ||
| 33 | class System; | ||
| 34 | } | ||
| 35 | |||
| 36 | namespace InputCommon { | 32 | namespace InputCommon { |
| 37 | class InputSubsystem; | 33 | class InputSubsystem; |
| 38 | } | 34 | } |
| 39 | 35 | ||
| 40 | namespace InputCommon::Polling { | 36 | namespace InputCommon::Polling { |
| 41 | class DevicePoller; | 37 | enum class InputType; |
| 42 | enum class DeviceType; | ||
| 43 | } // namespace InputCommon::Polling | 38 | } // namespace InputCommon::Polling |
| 44 | 39 | ||
| 45 | namespace Ui { | 40 | namespace Ui { |
| 46 | class ConfigureInputPlayer; | 41 | class ConfigureInputPlayer; |
| 47 | } | 42 | } |
| 48 | 43 | ||
| 44 | namespace Core::HID { | ||
| 45 | class HIDCore; | ||
| 46 | class EmulatedController; | ||
| 47 | enum class NpadStyleIndex : u8; | ||
| 48 | } // namespace Core::HID | ||
| 49 | |||
| 49 | class ConfigureInputPlayer : public QWidget { | 50 | class ConfigureInputPlayer : public QWidget { |
| 50 | Q_OBJECT | 51 | Q_OBJECT |
| 51 | 52 | ||
| 52 | public: | 53 | public: |
| 53 | explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, | 54 | explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, |
| 54 | InputCommon::InputSubsystem* input_subsystem_, | 55 | InputCommon::InputSubsystem* input_subsystem_, |
| 55 | InputProfiles* profiles_, Core::System& system_, | 56 | InputProfiles* profiles_, Core::HID::HIDCore& hid_core_, |
| 56 | bool debug = false); | 57 | bool is_powered_on_, bool debug = false); |
| 57 | ~ConfigureInputPlayer() override; | 58 | ~ConfigureInputPlayer() override; |
| 58 | 59 | ||
| 59 | /// Save all button configurations to settings file. | 60 | /// Save all button configurations to settings file. |
| 60 | void ApplyConfiguration(); | 61 | void ApplyConfiguration(); |
| 61 | 62 | ||
| 62 | /** | ||
| 63 | * Attempts to connect the currently selected controller in the HID backend. | ||
| 64 | * This function will not do anything if it is not connected in the frontend. | ||
| 65 | */ | ||
| 66 | void TryConnectSelectedController(); | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Attempts to disconnect the currently selected controller in the HID backend. | ||
| 70 | * This function will not do anything if the configuration has not changed. | ||
| 71 | */ | ||
| 72 | void TryDisconnectSelectedController(); | ||
| 73 | |||
| 74 | /// Set the connection state checkbox (used to sync state). | 63 | /// Set the connection state checkbox (used to sync state). |
| 75 | void ConnectPlayer(bool connected); | 64 | void ConnectPlayer(bool connected); |
| 76 | 65 | ||
| @@ -104,6 +93,10 @@ protected: | |||
| 104 | void showEvent(QShowEvent* event) override; | 93 | void showEvent(QShowEvent* event) override; |
| 105 | 94 | ||
| 106 | private: | 95 | private: |
| 96 | QString ButtonToText(const Common::ParamPackage& param); | ||
| 97 | |||
| 98 | QString AnalogToText(const Common::ParamPackage& param, const std::string& dir); | ||
| 99 | |||
| 107 | void changeEvent(QEvent* event) override; | 100 | void changeEvent(QEvent* event) override; |
| 108 | void RetranslateUI(); | 101 | void RetranslateUI(); |
| 109 | 102 | ||
| @@ -113,7 +106,7 @@ private: | |||
| 113 | /// Called when the button was pressed. | 106 | /// Called when the button was pressed. |
| 114 | void HandleClick(QPushButton* button, std::size_t button_id, | 107 | void HandleClick(QPushButton* button, std::size_t button_id, |
| 115 | std::function<void(const Common::ParamPackage&)> new_input_setter, | 108 | std::function<void(const Common::ParamPackage&)> new_input_setter, |
| 116 | InputCommon::Polling::DeviceType type); | 109 | InputCommon::Polling::InputType type); |
| 117 | 110 | ||
| 118 | /// Finish polling and configure input using the input_setter. | 111 | /// Finish polling and configure input using the input_setter. |
| 119 | void SetPollingResult(const Common::ParamPackage& params, bool abort); | 112 | void SetPollingResult(const Common::ParamPackage& params, bool abort); |
| @@ -134,17 +127,14 @@ private: | |||
| 134 | void SetConnectableControllers(); | 127 | void SetConnectableControllers(); |
| 135 | 128 | ||
| 136 | /// Gets the Controller Type for a given controller combobox index. | 129 | /// Gets the Controller Type for a given controller combobox index. |
| 137 | Settings::ControllerType GetControllerTypeFromIndex(int index) const; | 130 | Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index) const; |
| 138 | 131 | ||
| 139 | /// Gets the controller combobox index for a given Controller Type. | 132 | /// Gets the controller combobox index for a given Controller Type. |
| 140 | int GetIndexFromControllerType(Settings::ControllerType type) const; | 133 | int GetIndexFromControllerType(Core::HID::NpadStyleIndex type) const; |
| 141 | 134 | ||
| 142 | /// Update the available input devices. | 135 | /// Update the available input devices. |
| 143 | void UpdateInputDevices(); | 136 | void UpdateInputDevices(); |
| 144 | 137 | ||
| 145 | /// Update the current controller icon. | ||
| 146 | void UpdateControllerIcon(); | ||
| 147 | |||
| 148 | /// Hides and disables controller settings based on the current controller type. | 138 | /// Hides and disables controller settings based on the current controller type. |
| 149 | void UpdateControllerAvailableButtons(); | 139 | void UpdateControllerAvailableButtons(); |
| 150 | 140 | ||
| @@ -176,6 +166,7 @@ private: | |||
| 176 | 166 | ||
| 177 | std::size_t player_index; | 167 | std::size_t player_index; |
| 178 | bool debug; | 168 | bool debug; |
| 169 | bool is_powered_on; | ||
| 179 | 170 | ||
| 180 | InputCommon::InputSubsystem* input_subsystem; | 171 | InputCommon::InputSubsystem* input_subsystem; |
| 181 | 172 | ||
| @@ -185,7 +176,7 @@ private: | |||
| 185 | std::unique_ptr<QTimer> poll_timer; | 176 | std::unique_ptr<QTimer> poll_timer; |
| 186 | 177 | ||
| 187 | /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum. | 178 | /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum. |
| 188 | std::vector<std::pair<int, Settings::ControllerType>> index_controller_type_pairs; | 179 | std::vector<std::pair<int, Core::HID::NpadStyleIndex>> index_controller_type_pairs; |
| 189 | 180 | ||
| 190 | static constexpr int PLAYER_COUNT = 8; | 181 | static constexpr int PLAYER_COUNT = 8; |
| 191 | std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; | 182 | std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; |
| @@ -193,9 +184,7 @@ private: | |||
| 193 | /// This will be the the setting function when an input is awaiting configuration. | 184 | /// This will be the the setting function when an input is awaiting configuration. |
| 194 | std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; | 185 | std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; |
| 195 | 186 | ||
| 196 | std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; | 187 | Core::HID::EmulatedController* emulated_controller; |
| 197 | std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; | ||
| 198 | std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param; | ||
| 199 | 188 | ||
| 200 | static constexpr int ANALOG_SUB_BUTTONS_NUM = 4; | 189 | static constexpr int ANALOG_SUB_BUTTONS_NUM = 4; |
| 201 | 190 | ||
| @@ -221,15 +210,9 @@ private: | |||
| 221 | 210 | ||
| 222 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; | 211 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; |
| 223 | 212 | ||
| 224 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; | ||
| 225 | |||
| 226 | /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. | 213 | /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. |
| 227 | bool map_analog_stick_accepted{}; | 214 | bool map_analog_stick_accepted{}; |
| 228 | 215 | ||
| 229 | /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, | ||
| 230 | /// keyboard events are ignored. | ||
| 231 | bool want_keyboard_mouse{}; | ||
| 232 | |||
| 233 | /// List of physical devices users can map with. If a SDL backed device is selected, then you | 216 | /// List of physical devices users can map with. If a SDL backed device is selected, then you |
| 234 | /// can use this device to get a default mapping. | 217 | /// can use this device to get a default mapping. |
| 235 | std::vector<Common::ParamPackage> input_devices; | 218 | std::vector<Common::ParamPackage> input_devices; |
| @@ -239,5 +222,5 @@ private: | |||
| 239 | /// parent of the widget to this widget (but thats fine). | 222 | /// parent of the widget to this widget (but thats fine). |
| 240 | QWidget* bottom_row; | 223 | QWidget* bottom_row; |
| 241 | 224 | ||
| 242 | Core::System& system; | 225 | Core::HID::HIDCore& hid_core; |
| 243 | }; | 226 | }; |
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index e7433912b..756a414b5 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -89,31 +89,6 @@ | |||
| 89 | <height>21</height> | 89 | <height>21</height> |
| 90 | </size> | 90 | </size> |
| 91 | </property> | 91 | </property> |
| 92 | <item> | ||
| 93 | <property name="text"> | ||
| 94 | <string>Pro Controller</string> | ||
| 95 | </property> | ||
| 96 | </item> | ||
| 97 | <item> | ||
| 98 | <property name="text"> | ||
| 99 | <string>Dual Joycons</string> | ||
| 100 | </property> | ||
| 101 | </item> | ||
| 102 | <item> | ||
| 103 | <property name="text"> | ||
| 104 | <string>Left Joycon</string> | ||
| 105 | </property> | ||
| 106 | </item> | ||
| 107 | <item> | ||
| 108 | <property name="text"> | ||
| 109 | <string>Right Joycon</string> | ||
| 110 | </property> | ||
| 111 | </item> | ||
| 112 | <item> | ||
| 113 | <property name="text"> | ||
| 114 | <string>Handheld</string> | ||
| 115 | </property> | ||
| 116 | </item> | ||
| 117 | </widget> | 92 | </widget> |
| 118 | </item> | 93 | </item> |
| 119 | </layout> | 94 | </layout> |
| @@ -142,22 +117,9 @@ | |||
| 142 | </property> | 117 | </property> |
| 143 | <item> | 118 | <item> |
| 144 | <widget class="QComboBox" name="comboDevices"> | 119 | <widget class="QComboBox" name="comboDevices"> |
| 145 | <property name="minimumSize"> | 120 | <property name="minimumContentsLength"> |
| 146 | <size> | 121 | <number>60</number> |
| 147 | <width>0</width> | ||
| 148 | <height>21</height> | ||
| 149 | </size> | ||
| 150 | </property> | 122 | </property> |
| 151 | <item> | ||
| 152 | <property name="text"> | ||
| 153 | <string>Any</string> | ||
| 154 | </property> | ||
| 155 | </item> | ||
| 156 | <item> | ||
| 157 | <property name="text"> | ||
| 158 | <string>Keyboard/Mouse</string> | ||
| 159 | </property> | ||
| 160 | </item> | ||
| 161 | </widget> | 123 | </widget> |
| 162 | </item> | 124 | </item> |
| 163 | <item> | 125 | <item> |
| @@ -342,7 +304,7 @@ | |||
| 342 | <number>3</number> | 304 | <number>3</number> |
| 343 | </property> | 305 | </property> |
| 344 | <property name="topMargin"> | 306 | <property name="topMargin"> |
| 345 | <number>0</number> | 307 | <number>6</number> |
| 346 | </property> | 308 | </property> |
| 347 | <property name="rightMargin"> | 309 | <property name="rightMargin"> |
| 348 | <number>3</number> | 310 | <number>3</number> |
| @@ -918,7 +880,7 @@ | |||
| 918 | <number>3</number> | 880 | <number>3</number> |
| 919 | </property> | 881 | </property> |
| 920 | <property name="topMargin"> | 882 | <property name="topMargin"> |
| 921 | <number>0</number> | 883 | <number>6</number> |
| 922 | </property> | 884 | </property> |
| 923 | <property name="rightMargin"> | 885 | <property name="rightMargin"> |
| 924 | <number>3</number> | 886 | <number>3</number> |
| @@ -2221,7 +2183,7 @@ | |||
| 2221 | <number>3</number> | 2183 | <number>3</number> |
| 2222 | </property> | 2184 | </property> |
| 2223 | <property name="topMargin"> | 2185 | <property name="topMargin"> |
| 2224 | <number>0</number> | 2186 | <number>6</number> |
| 2225 | </property> | 2187 | </property> |
| 2226 | <property name="rightMargin"> | 2188 | <property name="rightMargin"> |
| 2227 | <number>3</number> | 2189 | <number>3</number> |
| @@ -2570,7 +2532,7 @@ | |||
| 2570 | <number>3</number> | 2532 | <number>3</number> |
| 2571 | </property> | 2533 | </property> |
| 2572 | <property name="topMargin"> | 2534 | <property name="topMargin"> |
| 2573 | <number>0</number> | 2535 | <number>6</number> |
| 2574 | </property> | 2536 | </property> |
| 2575 | <property name="rightMargin"> | 2537 | <property name="rightMargin"> |
| 2576 | <number>3</number> | 2538 | <number>3</number> |
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index f31f86339..6630321cb 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp | |||
| @@ -6,10 +6,12 @@ | |||
| 6 | #include <QMenu> | 6 | #include <QMenu> |
| 7 | #include <QPainter> | 7 | #include <QPainter> |
| 8 | #include <QTimer> | 8 | #include <QTimer> |
| 9 | |||
| 10 | #include "core/hid/emulated_controller.h" | ||
| 9 | #include "yuzu/configuration/configure_input_player_widget.h" | 11 | #include "yuzu/configuration/configure_input_player_widget.h" |
| 10 | 12 | ||
| 11 | PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) { | 13 | PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) { |
| 12 | UpdateColors(); | 14 | is_controller_set = false; |
| 13 | QTimer* timer = new QTimer(this); | 15 | QTimer* timer = new QTimer(this); |
| 14 | connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput)); | 16 | connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput)); |
| 15 | 17 | ||
| @@ -17,91 +19,37 @@ PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) { | |||
| 17 | timer->start(16); | 19 | timer->start(16); |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 20 | PlayerControlPreview::~PlayerControlPreview() = default; | 22 | PlayerControlPreview::~PlayerControlPreview() { |
| 21 | 23 | UnloadController(); | |
| 22 | void PlayerControlPreview::SetPlayerInput(std::size_t index, const ButtonParam& buttons_param, | 24 | }; |
| 23 | const AnalogParam& analogs_param) { | ||
| 24 | player_index = index; | ||
| 25 | Settings::ButtonsRaw buttonss; | ||
| 26 | Settings::AnalogsRaw analogs; | ||
| 27 | std::transform(buttons_param.begin(), buttons_param.end(), buttonss.begin(), | ||
| 28 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 29 | std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(), | ||
| 30 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 31 | |||
| 32 | std::transform(buttonss.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, | ||
| 33 | buttonss.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(), | ||
| 34 | Input::CreateDevice<Input::ButtonDevice>); | ||
| 35 | std::transform(analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, | ||
| 36 | analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(), | ||
| 37 | Input::CreateDevice<Input::AnalogDevice>); | ||
| 38 | UpdateColors(); | ||
| 39 | } | ||
| 40 | void PlayerControlPreview::SetPlayerInputRaw(std::size_t index, | ||
| 41 | const Settings::ButtonsRaw& buttons_, | ||
| 42 | Settings::AnalogsRaw analogs_) { | ||
| 43 | player_index = index; | ||
| 44 | std::transform(buttons_.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, | ||
| 45 | buttons_.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(), | ||
| 46 | Input::CreateDevice<Input::ButtonDevice>); | ||
| 47 | std::transform(analogs_.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, | ||
| 48 | analogs_.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(), | ||
| 49 | Input::CreateDevice<Input::AnalogDevice>); | ||
| 50 | UpdateColors(); | ||
| 51 | } | ||
| 52 | |||
| 53 | PlayerControlPreview::LedPattern PlayerControlPreview::GetColorPattern(std::size_t index, | ||
| 54 | bool player_on) { | ||
| 55 | if (!player_on) { | ||
| 56 | return {0, 0, 0, 0}; | ||
| 57 | } | ||
| 58 | |||
| 59 | switch (index) { | ||
| 60 | case 0: | ||
| 61 | return {1, 0, 0, 0}; | ||
| 62 | case 1: | ||
| 63 | return {1, 1, 0, 0}; | ||
| 64 | case 2: | ||
| 65 | return {1, 1, 1, 0}; | ||
| 66 | case 3: | ||
| 67 | return {1, 1, 1, 1}; | ||
| 68 | case 4: | ||
| 69 | return {1, 0, 0, 1}; | ||
| 70 | case 5: | ||
| 71 | return {1, 0, 1, 0}; | ||
| 72 | case 6: | ||
| 73 | return {1, 0, 1, 1}; | ||
| 74 | case 7: | ||
| 75 | return {0, 1, 1, 0}; | ||
| 76 | default: | ||
| 77 | return {0, 0, 0, 0}; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | void PlayerControlPreview::SetConnectedStatus(bool checked) { | ||
| 82 | LedPattern led_pattern = GetColorPattern(player_index, checked); | ||
| 83 | 25 | ||
| 84 | led_color[0] = led_pattern.position1 ? colors.led_on : colors.led_off; | 26 | void PlayerControlPreview::SetController(Core::HID::EmulatedController* controller_) { |
| 85 | led_color[1] = led_pattern.position2 ? colors.led_on : colors.led_off; | 27 | UnloadController(); |
| 86 | led_color[2] = led_pattern.position3 ? colors.led_on : colors.led_off; | 28 | is_controller_set = true; |
| 87 | led_color[3] = led_pattern.position4 ? colors.led_on : colors.led_off; | 29 | controller = controller_; |
| 88 | is_enabled = checked; | 30 | Core::HID::ControllerUpdateCallback engine_callback{ |
| 89 | ResetInputs(); | 31 | .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); }, |
| 32 | .is_npad_service = false, | ||
| 33 | }; | ||
| 34 | callback_key = controller->SetCallback(engine_callback); | ||
| 35 | ControllerUpdate(Core::HID::ControllerTriggerType::All); | ||
| 90 | } | 36 | } |
| 91 | 37 | ||
| 92 | void PlayerControlPreview::SetControllerType(const Settings::ControllerType type) { | 38 | void PlayerControlPreview::UnloadController() { |
| 93 | controller_type = type; | 39 | if (is_controller_set) { |
| 94 | UpdateColors(); | 40 | controller->DeleteCallback(callback_key); |
| 41 | is_controller_set = false; | ||
| 42 | } | ||
| 95 | } | 43 | } |
| 96 | 44 | ||
| 97 | void PlayerControlPreview::BeginMappingButton(std::size_t index) { | 45 | void PlayerControlPreview::BeginMappingButton(std::size_t button_id) { |
| 98 | button_mapping_index = index; | 46 | button_mapping_index = button_id; |
| 99 | mapping_active = true; | 47 | mapping_active = true; |
| 100 | } | 48 | } |
| 101 | 49 | ||
| 102 | void PlayerControlPreview::BeginMappingAnalog(std::size_t index) { | 50 | void PlayerControlPreview::BeginMappingAnalog(std::size_t stick_id) { |
| 103 | button_mapping_index = Settings::NativeButton::LStick + index; | 51 | button_mapping_index = Settings::NativeButton::LStick + stick_id; |
| 104 | analog_mapping_index = index; | 52 | analog_mapping_index = stick_id; |
| 105 | mapping_active = true; | 53 | mapping_active = true; |
| 106 | } | 54 | } |
| 107 | 55 | ||
| @@ -157,84 +105,109 @@ void PlayerControlPreview::UpdateColors() { | |||
| 157 | colors.left = colors.primary; | 105 | colors.left = colors.primary; |
| 158 | colors.right = colors.primary; | 106 | colors.right = colors.primary; |
| 159 | // Possible alternative to set colors from settings | 107 | // Possible alternative to set colors from settings |
| 160 | // colors.left = QColor(Settings::values.players.GetValue()[player_index].body_color_left); | 108 | // colors.left = QColor(controller->GetColors().left.body); |
| 161 | // colors.right = QColor(Settings::values.players.GetValue()[player_index].body_color_right); | 109 | // colors.right = QColor(controller->GetColors().right.body); |
| 162 | } | 110 | } |
| 163 | 111 | ||
| 164 | void PlayerControlPreview::ResetInputs() { | 112 | void PlayerControlPreview::ResetInputs() { |
| 165 | for (std::size_t index = 0; index < button_values.size(); ++index) { | 113 | button_values.fill({ |
| 166 | button_values[index] = false; | 114 | .value = false, |
| 167 | } | 115 | }); |
| 168 | 116 | stick_values.fill({ | |
| 169 | for (std::size_t index = 0; index < axis_values.size(); ++index) { | 117 | .x = {.value = 0, .properties = {0, 1, 0}}, |
| 170 | axis_values[index].properties = {0, 1, 0}; | 118 | .y = {.value = 0, .properties = {0, 1, 0}}, |
| 171 | axis_values[index].value = {0, 0}; | 119 | }); |
| 172 | axis_values[index].raw_value = {0, 0}; | 120 | trigger_values.fill({ |
| 173 | } | 121 | .analog = {.value = 0, .properties = {0, 1, 0}}, |
| 122 | .pressed = {.value = false}, | ||
| 123 | }); | ||
| 174 | update(); | 124 | update(); |
| 175 | } | 125 | } |
| 176 | 126 | ||
| 177 | void PlayerControlPreview::UpdateInput() { | 127 | void PlayerControlPreview::ControllerUpdate(Core::HID::ControllerTriggerType type) { |
| 178 | if (!is_enabled && !mapping_active && !Settings::values.tas_enable) { | 128 | if (type == Core::HID::ControllerTriggerType::All) { |
| 129 | ControllerUpdate(Core::HID::ControllerTriggerType::Color); | ||
| 130 | ControllerUpdate(Core::HID::ControllerTriggerType::Type); | ||
| 131 | ControllerUpdate(Core::HID::ControllerTriggerType::Connected); | ||
| 132 | ControllerUpdate(Core::HID::ControllerTriggerType::Button); | ||
| 133 | ControllerUpdate(Core::HID::ControllerTriggerType::Stick); | ||
| 134 | ControllerUpdate(Core::HID::ControllerTriggerType::Trigger); | ||
| 135 | ControllerUpdate(Core::HID::ControllerTriggerType::Battery); | ||
| 179 | return; | 136 | return; |
| 180 | } | 137 | } |
| 181 | bool input_changed = false; | 138 | |
| 182 | const auto& button_state = buttons; | 139 | switch (type) { |
| 183 | for (std::size_t index = 0; index < button_values.size(); ++index) { | 140 | case Core::HID::ControllerTriggerType::Connected: |
| 184 | bool value = false; | 141 | is_connected = true; |
| 185 | if (index < Settings::NativeButton::BUTTON_NS_END) { | 142 | led_pattern = controller->GetLedPattern(); |
| 186 | value = button_state[index]->GetStatus(); | 143 | needs_redraw = true; |
| 187 | } | 144 | break; |
| 188 | bool blink = mapping_active && index == button_mapping_index; | 145 | case Core::HID::ControllerTriggerType::Disconnected: |
| 189 | if (analog_mapping_index == Settings::NativeAnalog::NUM_STICKS_HID) { | 146 | is_connected = false; |
| 190 | blink &= blink_counter > 25; | 147 | led_pattern.raw = 0; |
| 191 | } | 148 | needs_redraw = true; |
| 192 | if (button_values[index] != value || blink) { | 149 | break; |
| 193 | input_changed = true; | 150 | case Core::HID::ControllerTriggerType::Type: |
| 194 | } | 151 | controller_type = controller->GetNpadStyleIndex(true); |
| 195 | button_values[index] = value || blink; | 152 | needs_redraw = true; |
| 153 | break; | ||
| 154 | case Core::HID::ControllerTriggerType::Color: | ||
| 155 | UpdateColors(); | ||
| 156 | needs_redraw = true; | ||
| 157 | break; | ||
| 158 | case Core::HID::ControllerTriggerType::Button: | ||
| 159 | button_values = controller->GetButtonsValues(); | ||
| 160 | needs_redraw = true; | ||
| 161 | break; | ||
| 162 | case Core::HID::ControllerTriggerType::Stick: | ||
| 163 | using namespace Settings::NativeAnalog; | ||
| 164 | stick_values = controller->GetSticksValues(); | ||
| 165 | // Y axis is inverted | ||
| 166 | stick_values[LStick].y.value = -stick_values[LStick].y.value; | ||
| 167 | stick_values[LStick].y.raw_value = -stick_values[LStick].y.raw_value; | ||
| 168 | stick_values[RStick].y.value = -stick_values[RStick].y.value; | ||
| 169 | stick_values[RStick].y.raw_value = -stick_values[RStick].y.raw_value; | ||
| 170 | needs_redraw = true; | ||
| 171 | break; | ||
| 172 | case Core::HID::ControllerTriggerType::Trigger: | ||
| 173 | trigger_values = controller->GetTriggersValues(); | ||
| 174 | needs_redraw = true; | ||
| 175 | break; | ||
| 176 | case Core::HID::ControllerTriggerType::Battery: | ||
| 177 | battery_values = controller->GetBatteryValues(); | ||
| 178 | needs_redraw = true; | ||
| 179 | break; | ||
| 180 | default: | ||
| 181 | break; | ||
| 196 | } | 182 | } |
| 183 | } | ||
| 197 | 184 | ||
| 198 | const auto& analog_state = sticks; | 185 | void PlayerControlPreview::UpdateInput() { |
| 199 | for (std::size_t index = 0; index < axis_values.size(); ++index) { | 186 | if (mapping_active) { |
| 200 | const auto [stick_x_f, stick_y_f] = analog_state[index]->GetStatus(); | ||
| 201 | const auto [stick_x_rf, stick_y_rf] = analog_state[index]->GetRawStatus(); | ||
| 202 | 187 | ||
| 203 | if (static_cast<int>(stick_x_rf * 45) != | 188 | for (std::size_t index = 0; index < button_values.size(); ++index) { |
| 204 | static_cast<int>(axis_values[index].raw_value.x() * 45) || | 189 | bool blink = index == button_mapping_index; |
| 205 | static_cast<int>(-stick_y_rf * 45) != | 190 | if (analog_mapping_index == Settings::NativeAnalog::NumAnalogs) { |
| 206 | static_cast<int>(axis_values[index].raw_value.y() * 45)) { | 191 | blink &= blink_counter > 25; |
| 207 | input_changed = true; | 192 | } |
| 193 | if (button_values[index].value != blink) { | ||
| 194 | needs_redraw = true; | ||
| 195 | } | ||
| 196 | button_values[index].value = blink; | ||
| 208 | } | 197 | } |
| 209 | 198 | ||
| 210 | axis_values[index].properties = analog_state[index]->GetAnalogProperties(); | 199 | for (std::size_t index = 0; index < stick_values.size(); ++index) { |
| 211 | axis_values[index].value = QPointF(stick_x_f, -stick_y_f); | 200 | const bool blink_analog = index == analog_mapping_index; |
| 212 | axis_values[index].raw_value = QPointF(stick_x_rf, -stick_y_rf); | 201 | if (blink_analog) { |
| 213 | 202 | needs_redraw = true; | |
| 214 | const bool blink_analog = mapping_active && index == analog_mapping_index; | 203 | stick_values[index].x.value = blink_counter < 25 ? -blink_counter / 25.0f : 0; |
| 215 | if (blink_analog) { | 204 | stick_values[index].y.value = |
| 216 | input_changed = true; | 205 | blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0; |
| 217 | axis_values[index].value = | 206 | } |
| 218 | QPointF(blink_counter < 25 ? -blink_counter / 25.0f : 0, | ||
| 219 | blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0); | ||
| 220 | } | 207 | } |
| 221 | } | 208 | } |
| 222 | 209 | if (needs_redraw) { | |
| 223 | if (input_changed) { | ||
| 224 | update(); | 210 | update(); |
| 225 | if (controller_callback.input != nullptr) { | ||
| 226 | ControllerInput input{ | ||
| 227 | .axis_values = {std::pair<float, float>{ | ||
| 228 | axis_values[Settings::NativeAnalog::LStick].value.x(), | ||
| 229 | axis_values[Settings::NativeAnalog::LStick].value.y()}, | ||
| 230 | std::pair<float, float>{ | ||
| 231 | axis_values[Settings::NativeAnalog::RStick].value.x(), | ||
| 232 | axis_values[Settings::NativeAnalog::RStick].value.y()}}, | ||
| 233 | .button_values = button_values, | ||
| 234 | .changed = true, | ||
| 235 | }; | ||
| 236 | controller_callback.input(std::move(input)); | ||
| 237 | } | ||
| 238 | } | 211 | } |
| 239 | 212 | ||
| 240 | if (mapping_active) { | 213 | if (mapping_active) { |
| @@ -242,10 +215,6 @@ void PlayerControlPreview::UpdateInput() { | |||
| 242 | } | 215 | } |
| 243 | } | 216 | } |
| 244 | 217 | ||
| 245 | void PlayerControlPreview::SetCallBack(ControllerCallback callback_) { | ||
| 246 | controller_callback = std::move(callback_); | ||
| 247 | } | ||
| 248 | |||
| 249 | void PlayerControlPreview::paintEvent(QPaintEvent* event) { | 218 | void PlayerControlPreview::paintEvent(QPaintEvent* event) { |
| 250 | QFrame::paintEvent(event); | 219 | QFrame::paintEvent(event); |
| 251 | QPainter p(this); | 220 | QPainter p(this); |
| @@ -253,22 +222,22 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) { | |||
| 253 | const QPointF center = rect().center(); | 222 | const QPointF center = rect().center(); |
| 254 | 223 | ||
| 255 | switch (controller_type) { | 224 | switch (controller_type) { |
| 256 | case Settings::ControllerType::Handheld: | 225 | case Core::HID::NpadStyleIndex::Handheld: |
| 257 | DrawHandheldController(p, center); | 226 | DrawHandheldController(p, center); |
| 258 | break; | 227 | break; |
| 259 | case Settings::ControllerType::DualJoyconDetached: | 228 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 260 | DrawDualController(p, center); | 229 | DrawDualController(p, center); |
| 261 | break; | 230 | break; |
| 262 | case Settings::ControllerType::LeftJoycon: | 231 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 263 | DrawLeftController(p, center); | 232 | DrawLeftController(p, center); |
| 264 | break; | 233 | break; |
| 265 | case Settings::ControllerType::RightJoycon: | 234 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 266 | DrawRightController(p, center); | 235 | DrawRightController(p, center); |
| 267 | break; | 236 | break; |
| 268 | case Settings::ControllerType::GameCube: | 237 | case Core::HID::NpadStyleIndex::GameCube: |
| 269 | DrawGCController(p, center); | 238 | DrawGCController(p, center); |
| 270 | break; | 239 | break; |
| 271 | case Settings::ControllerType::ProController: | 240 | case Core::HID::NpadStyleIndex::ProController: |
| 272 | default: | 241 | default: |
| 273 | DrawProController(p, center); | 242 | DrawProController(p, center); |
| 274 | break; | 243 | break; |
| @@ -281,7 +250,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) | |||
| 281 | 250 | ||
| 282 | // Sideview left joystick | 251 | // Sideview left joystick |
| 283 | DrawJoystickSideview(p, center + QPoint(142, -69), | 252 | DrawJoystickSideview(p, center + QPoint(142, -69), |
| 284 | -axis_values[Settings::NativeAnalog::LStick].value.y(), 1.15f, | 253 | -stick_values[Settings::NativeAnalog::LStick].y.value, 1.15f, |
| 285 | button_values[LStick]); | 254 | button_values[LStick]); |
| 286 | 255 | ||
| 287 | // Topview D-pad buttons | 256 | // Topview D-pad buttons |
| @@ -292,7 +261,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) | |||
| 292 | 261 | ||
| 293 | // Topview left joystick | 262 | // Topview left joystick |
| 294 | DrawJoystickSideview(p, center + QPointF(-140.5f, -28), | 263 | DrawJoystickSideview(p, center + QPointF(-140.5f, -28), |
| 295 | -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1.15f, | 264 | -stick_values[Settings::NativeAnalog::LStick].x.value + 15.0f, 1.15f, |
| 296 | button_values[LStick]); | 265 | button_values[LStick]); |
| 297 | 266 | ||
| 298 | // Topview minus button | 267 | // Topview minus button |
| @@ -334,8 +303,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) | |||
| 334 | { | 303 | { |
| 335 | // Draw joysticks | 304 | // Draw joysticks |
| 336 | using namespace Settings::NativeAnalog; | 305 | using namespace Settings::NativeAnalog; |
| 337 | DrawJoystick(p, center + QPointF(9, -69) + (axis_values[LStick].value * 8), 1.8f, | 306 | DrawJoystick(p, |
| 338 | button_values[Settings::NativeButton::LStick]); | 307 | center + QPointF(9, -69) + |
| 308 | (QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value) * 8), | ||
| 309 | 1.8f, button_values[Settings::NativeButton::LStick]); | ||
| 339 | DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0)); | 310 | DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0)); |
| 340 | } | 311 | } |
| 341 | 312 | ||
| @@ -384,6 +355,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) | |||
| 384 | p.setPen(colors.font2); | 355 | p.setPen(colors.font2); |
| 385 | p.setBrush(colors.font2); | 356 | p.setBrush(colors.font2); |
| 386 | DrawCircle(p, center + QPoint(26, 71), 5); | 357 | DrawCircle(p, center + QPoint(26, 71), 5); |
| 358 | |||
| 359 | // Draw battery | ||
| 360 | DrawBattery(p, center + QPoint(-170, -140), | ||
| 361 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | ||
| 387 | } | 362 | } |
| 388 | 363 | ||
| 389 | void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center) { | 364 | void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center) { |
| @@ -392,20 +367,22 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center | |||
| 392 | 367 | ||
| 393 | // Sideview right joystick | 368 | // Sideview right joystick |
| 394 | DrawJoystickSideview(p, center + QPoint(173 - 315, 11), | 369 | DrawJoystickSideview(p, center + QPoint(173 - 315, 11), |
| 395 | axis_values[Settings::NativeAnalog::RStick].value.y() + 10.0f, 1.15f, | 370 | stick_values[Settings::NativeAnalog::RStick].y.value + 10.0f, 1.15f, |
| 396 | button_values[Settings::NativeButton::RStick]); | 371 | button_values[Settings::NativeButton::RStick]); |
| 397 | 372 | ||
| 373 | // Topview right joystick | ||
| 374 | DrawJoystickSideview(p, center + QPointF(140, -28), | ||
| 375 | -stick_values[Settings::NativeAnalog::RStick].x.value + 15.0f, 1.15f, | ||
| 376 | button_values[RStick]); | ||
| 377 | |||
| 398 | // Topview face buttons | 378 | // Topview face buttons |
| 399 | p.setPen(colors.outline); | 379 | p.setPen(colors.outline); |
| 400 | button_color = colors.button; | 380 | button_color = colors.button; |
| 401 | DrawRoundButton(p, center + QPoint(163, -21), button_values[A], 11, 5, Direction::Up); | 381 | DrawRoundButton(p, center + QPoint(163, -21), button_values[A], 11, 5, Direction::Up); |
| 382 | DrawRoundButton(p, center + QPoint(140, -21), button_values[B], 11, 5, Direction::Up); | ||
| 383 | DrawRoundButton(p, center + QPoint(140, -21), button_values[X], 11, 5, Direction::Up); | ||
| 402 | DrawRoundButton(p, center + QPoint(117, -21), button_values[Y], 11, 5, Direction::Up); | 384 | DrawRoundButton(p, center + QPoint(117, -21), button_values[Y], 11, 5, Direction::Up); |
| 403 | 385 | ||
| 404 | // Topview right joystick | ||
| 405 | DrawJoystickSideview(p, center + QPointF(140, -28), | ||
| 406 | -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1.15f, | ||
| 407 | button_values[RStick]); | ||
| 408 | |||
| 409 | // Topview plus button | 386 | // Topview plus button |
| 410 | p.setPen(colors.outline); | 387 | p.setPen(colors.outline); |
| 411 | button_color = colors.button; | 388 | button_color = colors.button; |
| @@ -448,8 +425,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center | |||
| 448 | { | 425 | { |
| 449 | // Draw joysticks | 426 | // Draw joysticks |
| 450 | using namespace Settings::NativeAnalog; | 427 | using namespace Settings::NativeAnalog; |
| 451 | DrawJoystick(p, center + QPointF(-9, 11) + (axis_values[RStick].value * 8), 1.8f, | 428 | DrawJoystick(p, |
| 452 | button_values[Settings::NativeButton::RStick]); | 429 | center + QPointF(-9, 11) + |
| 430 | (QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value) * 8), | ||
| 431 | 1.8f, button_values[Settings::NativeButton::RStick]); | ||
| 453 | DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90)); | 432 | DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90)); |
| 454 | } | 433 | } |
| 455 | 434 | ||
| @@ -503,6 +482,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center | |||
| 503 | p.setPen(colors.transparent); | 482 | p.setPen(colors.transparent); |
| 504 | p.setBrush(colors.font2); | 483 | p.setBrush(colors.font2); |
| 505 | DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); | 484 | DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); |
| 485 | |||
| 486 | // Draw battery | ||
| 487 | DrawBattery(p, center + QPoint(110, -140), | ||
| 488 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); | ||
| 506 | } | 489 | } |
| 507 | 490 | ||
| 508 | void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) { | 491 | void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) { |
| @@ -512,17 +495,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) | |||
| 512 | // Left/Right trigger | 495 | // Left/Right trigger |
| 513 | DrawDualTriggers(p, center, button_values[L], button_values[R]); | 496 | DrawDualTriggers(p, center, button_values[L], button_values[R]); |
| 514 | 497 | ||
| 498 | // Topview right joystick | ||
| 499 | DrawJoystickSideview(p, center + QPointF(180, -78), | ||
| 500 | -stick_values[Settings::NativeAnalog::RStick].x.value + 15.0f, 1, | ||
| 501 | button_values[RStick]); | ||
| 502 | |||
| 515 | // Topview face buttons | 503 | // Topview face buttons |
| 516 | p.setPen(colors.outline); | 504 | p.setPen(colors.outline); |
| 517 | button_color = colors.button; | 505 | button_color = colors.button; |
| 518 | DrawRoundButton(p, center + QPoint(200, -71), button_values[A], 10, 5, Direction::Up); | 506 | DrawRoundButton(p, center + QPoint(200, -71), button_values[A], 10, 5, Direction::Up); |
| 507 | DrawRoundButton(p, center + QPoint(180, -71), button_values[B], 10, 5, Direction::Up); | ||
| 508 | DrawRoundButton(p, center + QPoint(180, -71), button_values[X], 10, 5, Direction::Up); | ||
| 519 | DrawRoundButton(p, center + QPoint(160, -71), button_values[Y], 10, 5, Direction::Up); | 509 | DrawRoundButton(p, center + QPoint(160, -71), button_values[Y], 10, 5, Direction::Up); |
| 520 | 510 | ||
| 521 | // Topview right joystick | ||
| 522 | DrawJoystickSideview(p, center + QPointF(180, -78), | ||
| 523 | -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1, | ||
| 524 | button_values[RStick]); | ||
| 525 | |||
| 526 | // Topview plus button | 511 | // Topview plus button |
| 527 | p.setPen(colors.outline); | 512 | p.setPen(colors.outline); |
| 528 | button_color = colors.button; | 513 | button_color = colors.button; |
| @@ -538,7 +523,7 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) | |||
| 538 | 523 | ||
| 539 | // Topview left joystick | 524 | // Topview left joystick |
| 540 | DrawJoystickSideview(p, center + QPointF(-180.5f, -78), | 525 | DrawJoystickSideview(p, center + QPointF(-180.5f, -78), |
| 541 | -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1, | 526 | -stick_values[Settings::NativeAnalog::LStick].x.value + 15.0f, 1, |
| 542 | button_values[LStick]); | 527 | button_values[LStick]); |
| 543 | 528 | ||
| 544 | // Topview minus button | 529 | // Topview minus button |
| @@ -557,13 +542,13 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) | |||
| 557 | { | 542 | { |
| 558 | // Draw joysticks | 543 | // Draw joysticks |
| 559 | using namespace Settings::NativeAnalog; | 544 | using namespace Settings::NativeAnalog; |
| 560 | const auto& l_stick = axis_values[LStick]; | 545 | const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value); |
| 561 | const auto l_button = button_values[Settings::NativeButton::LStick]; | 546 | const auto l_button = button_values[Settings::NativeButton::LStick]; |
| 562 | const auto& r_stick = axis_values[RStick]; | 547 | const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value); |
| 563 | const auto r_button = button_values[Settings::NativeButton::RStick]; | 548 | const auto r_button = button_values[Settings::NativeButton::RStick]; |
| 564 | 549 | ||
| 565 | DrawJoystick(p, center + QPointF(-65, -65) + (l_stick.value * 7), 1.62f, l_button); | 550 | DrawJoystick(p, center + QPointF(-65, -65) + (l_stick * 7), 1.62f, l_button); |
| 566 | DrawJoystick(p, center + QPointF(65, 12) + (r_stick.value * 7), 1.62f, r_button); | 551 | DrawJoystick(p, center + QPointF(65, 12) + (r_stick * 7), 1.62f, r_button); |
| 567 | DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90)); | 552 | DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90)); |
| 568 | } | 553 | } |
| 569 | 554 | ||
| @@ -634,6 +619,12 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) | |||
| 634 | p.setPen(colors.transparent); | 619 | p.setPen(colors.transparent); |
| 635 | p.setBrush(colors.font2); | 620 | p.setBrush(colors.font2); |
| 636 | DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); | 621 | DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); |
| 622 | |||
| 623 | // Draw battery | ||
| 624 | DrawBattery(p, center + QPoint(-100, -160), | ||
| 625 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | ||
| 626 | DrawBattery(p, center + QPoint(40, -160), | ||
| 627 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); | ||
| 637 | } | 628 | } |
| 638 | 629 | ||
| 639 | void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF center) { | 630 | void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF center) { |
| @@ -643,13 +634,13 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen | |||
| 643 | { | 634 | { |
| 644 | // Draw joysticks | 635 | // Draw joysticks |
| 645 | using namespace Settings::NativeAnalog; | 636 | using namespace Settings::NativeAnalog; |
| 646 | const auto& l_stick = axis_values[LStick]; | 637 | const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value); |
| 647 | const auto l_button = button_values[Settings::NativeButton::LStick]; | 638 | const auto l_button = button_values[Settings::NativeButton::LStick]; |
| 648 | const auto& r_stick = axis_values[RStick]; | 639 | const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value); |
| 649 | const auto r_button = button_values[Settings::NativeButton::RStick]; | 640 | const auto r_button = button_values[Settings::NativeButton::RStick]; |
| 650 | 641 | ||
| 651 | DrawJoystick(p, center + QPointF(-171, -41) + (l_stick.value * 4), 1.0f, l_button); | 642 | DrawJoystick(p, center + QPointF(-171, -41) + (l_stick * 4), 1.0f, l_button); |
| 652 | DrawJoystick(p, center + QPointF(171, 8) + (r_stick.value * 4), 1.0f, r_button); | 643 | DrawJoystick(p, center + QPointF(171, 8) + (r_stick * 4), 1.0f, r_button); |
| 653 | DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0)); | 644 | DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0)); |
| 654 | } | 645 | } |
| 655 | 646 | ||
| @@ -732,6 +723,12 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen | |||
| 732 | p.setPen(colors.transparent); | 723 | p.setPen(colors.transparent); |
| 733 | p.setBrush(colors.font2); | 724 | p.setBrush(colors.font2); |
| 734 | DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); | 725 | DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); |
| 726 | |||
| 727 | // Draw battery | ||
| 728 | DrawBattery(p, center + QPoint(-200, 110), | ||
| 729 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | ||
| 730 | DrawBattery(p, center + QPoint(130, 110), | ||
| 731 | battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); | ||
| 735 | } | 732 | } |
| 736 | 733 | ||
| 737 | void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) { | 734 | void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) { |
| @@ -741,9 +738,11 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) | |||
| 741 | { | 738 | { |
| 742 | // Draw joysticks | 739 | // Draw joysticks |
| 743 | using namespace Settings::NativeAnalog; | 740 | using namespace Settings::NativeAnalog; |
| 744 | DrawProJoystick(p, center + QPointF(-111, -55), axis_values[LStick].value, 11, | 741 | const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value); |
| 742 | const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value); | ||
| 743 | DrawProJoystick(p, center + QPointF(-111, -55), l_stick, 11, | ||
| 745 | button_values[Settings::NativeButton::LStick]); | 744 | button_values[Settings::NativeButton::LStick]); |
| 746 | DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11, | 745 | DrawProJoystick(p, center + QPointF(51, 0), r_stick, 11, |
| 747 | button_values[Settings::NativeButton::RStick]); | 746 | button_values[Settings::NativeButton::RStick]); |
| 748 | DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105)); | 747 | DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105)); |
| 749 | } | 748 | } |
| @@ -817,24 +816,27 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) | |||
| 817 | p.setPen(colors.transparent); | 816 | p.setPen(colors.transparent); |
| 818 | p.setBrush(colors.font2); | 817 | p.setBrush(colors.font2); |
| 819 | DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); | 818 | DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); |
| 819 | |||
| 820 | // Draw battery | ||
| 821 | DrawBattery(p, center + QPoint(-30, -160), | ||
| 822 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | ||
| 820 | } | 823 | } |
| 821 | 824 | ||
| 822 | void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) { | 825 | void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) { |
| 823 | DrawGCTriggers(p, center, button_values[Settings::NativeButton::ZL], | 826 | DrawGCTriggers(p, center, trigger_values[0], trigger_values[1]); |
| 824 | button_values[Settings::NativeButton::ZR]); | ||
| 825 | DrawGCButtonZ(p, center, button_values[Settings::NativeButton::R]); | 827 | DrawGCButtonZ(p, center, button_values[Settings::NativeButton::R]); |
| 826 | DrawGCBody(p, center); | 828 | DrawGCBody(p, center); |
| 827 | { | 829 | { |
| 828 | // Draw joysticks | 830 | // Draw joysticks |
| 829 | using namespace Settings::NativeAnalog; | 831 | using namespace Settings::NativeAnalog; |
| 830 | DrawGCJoystick(p, center + QPointF(-111, -44) + (axis_values[LStick].value * 10), false); | 832 | const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value); |
| 833 | const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value); | ||
| 834 | DrawGCJoystick(p, center + QPointF(-111, -44) + (l_stick * 10), {}); | ||
| 831 | button_color = colors.button2; | 835 | button_color = colors.button2; |
| 832 | DrawCircleButton(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), false, | 836 | DrawCircleButton(p, center + QPointF(61, 37) + (r_stick * 9.5f), {}, 15); |
| 833 | 15); | ||
| 834 | p.setPen(colors.transparent); | 837 | p.setPen(colors.transparent); |
| 835 | p.setBrush(colors.font); | 838 | p.setBrush(colors.font); |
| 836 | DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C, | 839 | DrawSymbol(p, center + QPointF(61, 37) + (r_stick * 9.5f), Symbol::C, 1.0f); |
| 837 | 1.0f); | ||
| 838 | DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125)); | 840 | DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125)); |
| 839 | } | 841 | } |
| 840 | 842 | ||
| @@ -871,6 +873,10 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) { | |||
| 871 | // Minus and Plus buttons | 873 | // Minus and Plus buttons |
| 872 | p.setPen(colors.outline); | 874 | p.setPen(colors.outline); |
| 873 | DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); | 875 | DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); |
| 876 | |||
| 877 | // Draw battery | ||
| 878 | DrawBattery(p, center + QPoint(-30, -165), | ||
| 879 | battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); | ||
| 874 | } | 880 | } |
| 875 | 881 | ||
| 876 | constexpr std::array<float, 13 * 2> symbol_a = { | 882 | constexpr std::array<float, 13 * 2> symbol_a = { |
| @@ -1837,10 +1843,14 @@ void PlayerControlPreview::DrawLeftBody(QPainter& p, const QPointF center) { | |||
| 1837 | const float led_size = 5.0f; | 1843 | const float led_size = 5.0f; |
| 1838 | const QPointF led_position = sideview_center + QPointF(0, -36); | 1844 | const QPointF led_position = sideview_center + QPointF(0, -36); |
| 1839 | int led_count = 0; | 1845 | int led_count = 0; |
| 1840 | for (const auto& color : led_color) { | 1846 | p.setBrush(led_pattern.position1 ? colors.led_on : colors.led_off); |
| 1841 | p.setBrush(color); | 1847 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); |
| 1842 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); | 1848 | p.setBrush(led_pattern.position2 ? colors.led_on : colors.led_off); |
| 1843 | } | 1849 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); |
| 1850 | p.setBrush(led_pattern.position3 ? colors.led_on : colors.led_off); | ||
| 1851 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); | ||
| 1852 | p.setBrush(led_pattern.position4 ? colors.led_on : colors.led_off); | ||
| 1853 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); | ||
| 1844 | } | 1854 | } |
| 1845 | 1855 | ||
| 1846 | void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) { | 1856 | void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) { |
| @@ -1933,14 +1943,19 @@ void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) { | |||
| 1933 | const float led_size = 5.0f; | 1943 | const float led_size = 5.0f; |
| 1934 | const QPointF led_position = sideview_center + QPointF(0, -36); | 1944 | const QPointF led_position = sideview_center + QPointF(0, -36); |
| 1935 | int led_count = 0; | 1945 | int led_count = 0; |
| 1936 | for (const auto& color : led_color) { | 1946 | p.setBrush(led_pattern.position1 ? colors.led_on : colors.led_off); |
| 1937 | p.setBrush(color); | 1947 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); |
| 1938 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); | 1948 | p.setBrush(led_pattern.position2 ? colors.led_on : colors.led_off); |
| 1939 | } | 1949 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); |
| 1940 | } | 1950 | p.setBrush(led_pattern.position3 ? colors.led_on : colors.led_off); |
| 1941 | 1951 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); | |
| 1942 | void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bool left_pressed, | 1952 | p.setBrush(led_pattern.position4 ? colors.led_on : colors.led_off); |
| 1943 | bool right_pressed) { | 1953 | DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); |
| 1954 | } | ||
| 1955 | |||
| 1956 | void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, | ||
| 1957 | const Common::Input::ButtonStatus& left_pressed, | ||
| 1958 | const Common::Input::ButtonStatus& right_pressed) { | ||
| 1944 | std::array<QPointF, pro_left_trigger.size() / 2> qleft_trigger; | 1959 | std::array<QPointF, pro_left_trigger.size() / 2> qleft_trigger; |
| 1945 | std::array<QPointF, pro_left_trigger.size() / 2> qright_trigger; | 1960 | std::array<QPointF, pro_left_trigger.size() / 2> qright_trigger; |
| 1946 | std::array<QPointF, pro_body_top.size()> qbody_top; | 1961 | std::array<QPointF, pro_body_top.size()> qbody_top; |
| @@ -1949,8 +1964,10 @@ void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bo | |||
| 1949 | const float trigger_x = pro_left_trigger[point * 2 + 0]; | 1964 | const float trigger_x = pro_left_trigger[point * 2 + 0]; |
| 1950 | const float trigger_y = pro_left_trigger[point * 2 + 1]; | 1965 | const float trigger_y = pro_left_trigger[point * 2 + 1]; |
| 1951 | 1966 | ||
| 1952 | qleft_trigger[point] = center + QPointF(trigger_x, trigger_y + (left_pressed ? 2 : 0)); | 1967 | qleft_trigger[point] = |
| 1953 | qright_trigger[point] = center + QPointF(-trigger_x, trigger_y + (right_pressed ? 2 : 0)); | 1968 | center + QPointF(trigger_x, trigger_y + (left_pressed.value ? 2 : 0)); |
| 1969 | qright_trigger[point] = | ||
| 1970 | center + QPointF(-trigger_x, trigger_y + (right_pressed.value ? 2 : 0)); | ||
| 1954 | } | 1971 | } |
| 1955 | 1972 | ||
| 1956 | for (std::size_t point = 0; point < pro_body_top.size() / 2; ++point) { | 1973 | for (std::size_t point = 0; point < pro_body_top.size() / 2; ++point) { |
| @@ -1967,16 +1984,17 @@ void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bo | |||
| 1967 | DrawPolygon(p, qbody_top); | 1984 | DrawPolygon(p, qbody_top); |
| 1968 | 1985 | ||
| 1969 | // Left trigger | 1986 | // Left trigger |
| 1970 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 1987 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 1971 | DrawPolygon(p, qleft_trigger); | 1988 | DrawPolygon(p, qleft_trigger); |
| 1972 | 1989 | ||
| 1973 | // Right trigger | 1990 | // Right trigger |
| 1974 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 1991 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 1975 | DrawPolygon(p, qright_trigger); | 1992 | DrawPolygon(p, qright_trigger); |
| 1976 | } | 1993 | } |
| 1977 | 1994 | ||
| 1978 | void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, bool left_pressed, | 1995 | void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, |
| 1979 | bool right_pressed) { | 1996 | Common::Input::TriggerStatus left_trigger, |
| 1997 | Common::Input::TriggerStatus right_trigger) { | ||
| 1980 | std::array<QPointF, left_gc_trigger.size() / 2> qleft_trigger; | 1998 | std::array<QPointF, left_gc_trigger.size() / 2> qleft_trigger; |
| 1981 | std::array<QPointF, left_gc_trigger.size() / 2> qright_trigger; | 1999 | std::array<QPointF, left_gc_trigger.size() / 2> qright_trigger; |
| 1982 | 2000 | ||
| @@ -1984,32 +2002,37 @@ void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, boo | |||
| 1984 | const float trigger_x = left_gc_trigger[point * 2 + 0]; | 2002 | const float trigger_x = left_gc_trigger[point * 2 + 0]; |
| 1985 | const float trigger_y = left_gc_trigger[point * 2 + 1]; | 2003 | const float trigger_y = left_gc_trigger[point * 2 + 1]; |
| 1986 | 2004 | ||
| 1987 | qleft_trigger[point] = center + QPointF(trigger_x, trigger_y + (left_pressed ? 10 : 0)); | 2005 | qleft_trigger[point] = |
| 1988 | qright_trigger[point] = center + QPointF(-trigger_x, trigger_y + (right_pressed ? 10 : 0)); | 2006 | center + QPointF(trigger_x, trigger_y + (left_trigger.analog.value * 10.0f)); |
| 2007 | qright_trigger[point] = | ||
| 2008 | center + QPointF(-trigger_x, trigger_y + (right_trigger.analog.value * 10.0f)); | ||
| 1989 | } | 2009 | } |
| 1990 | 2010 | ||
| 1991 | // Left trigger | 2011 | // Left trigger |
| 1992 | p.setPen(colors.outline); | 2012 | p.setPen(colors.outline); |
| 1993 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2013 | p.setBrush(left_trigger.pressed.value ? colors.highlight : colors.button); |
| 1994 | DrawPolygon(p, qleft_trigger); | 2014 | DrawPolygon(p, qleft_trigger); |
| 1995 | 2015 | ||
| 1996 | // Right trigger | 2016 | // Right trigger |
| 1997 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2017 | p.setBrush(right_trigger.pressed.value ? colors.highlight : colors.button); |
| 1998 | DrawPolygon(p, qright_trigger); | 2018 | DrawPolygon(p, qright_trigger); |
| 1999 | 2019 | ||
| 2000 | // Draw L text | 2020 | // Draw L text |
| 2001 | p.setPen(colors.transparent); | 2021 | p.setPen(colors.transparent); |
| 2002 | p.setBrush(colors.font); | 2022 | p.setBrush(colors.font); |
| 2003 | DrawSymbol(p, center + QPointF(-132, -119 + (left_pressed ? 10 : 0)), Symbol::L, 1.7f); | 2023 | DrawSymbol(p, center + QPointF(-132, -119 + (left_trigger.analog.value * 10.0f)), Symbol::L, |
| 2024 | 1.7f); | ||
| 2004 | 2025 | ||
| 2005 | // Draw R text | 2026 | // Draw R text |
| 2006 | p.setPen(colors.transparent); | 2027 | p.setPen(colors.transparent); |
| 2007 | p.setBrush(colors.font); | 2028 | p.setBrush(colors.font); |
| 2008 | DrawSymbol(p, center + QPointF(121.5f, -119 + (right_pressed ? 10 : 0)), Symbol::R, 1.7f); | 2029 | DrawSymbol(p, center + QPointF(121.5f, -119 + (right_trigger.analog.value * 10.0f)), Symbol::R, |
| 2030 | 1.7f); | ||
| 2009 | } | 2031 | } |
| 2010 | 2032 | ||
| 2011 | void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF center, | 2033 | void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF center, |
| 2012 | bool left_pressed, bool right_pressed) { | 2034 | const Common::Input::ButtonStatus& left_pressed, |
| 2035 | const Common::Input::ButtonStatus& right_pressed) { | ||
| 2013 | std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; | 2036 | std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; |
| 2014 | std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; | 2037 | std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; |
| 2015 | 2038 | ||
| @@ -2018,23 +2041,24 @@ void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF cente | |||
| 2018 | const float left_trigger_y = left_joycon_trigger[point * 2 + 1]; | 2041 | const float left_trigger_y = left_joycon_trigger[point * 2 + 1]; |
| 2019 | 2042 | ||
| 2020 | qleft_trigger[point] = | 2043 | qleft_trigger[point] = |
| 2021 | center + QPointF(left_trigger_x, left_trigger_y + (left_pressed ? 0.5f : 0)); | 2044 | center + QPointF(left_trigger_x, left_trigger_y + (left_pressed.value ? 0.5f : 0)); |
| 2022 | qright_trigger[point] = | 2045 | qright_trigger[point] = |
| 2023 | center + QPointF(-left_trigger_x, left_trigger_y + (right_pressed ? 0.5f : 0)); | 2046 | center + QPointF(-left_trigger_x, left_trigger_y + (right_pressed.value ? 0.5f : 0)); |
| 2024 | } | 2047 | } |
| 2025 | 2048 | ||
| 2026 | // Left trigger | 2049 | // Left trigger |
| 2027 | p.setPen(colors.outline); | 2050 | p.setPen(colors.outline); |
| 2028 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2051 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 2029 | DrawPolygon(p, qleft_trigger); | 2052 | DrawPolygon(p, qleft_trigger); |
| 2030 | 2053 | ||
| 2031 | // Right trigger | 2054 | // Right trigger |
| 2032 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2055 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 2033 | DrawPolygon(p, qright_trigger); | 2056 | DrawPolygon(p, qright_trigger); |
| 2034 | } | 2057 | } |
| 2035 | 2058 | ||
| 2036 | void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, bool left_pressed, | 2059 | void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, |
| 2037 | bool right_pressed) { | 2060 | const Common::Input::ButtonStatus& left_pressed, |
| 2061 | const Common::Input::ButtonStatus& right_pressed) { | ||
| 2038 | std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; | 2062 | std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; |
| 2039 | std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; | 2063 | std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; |
| 2040 | constexpr float size = 1.62f; | 2064 | constexpr float size = 1.62f; |
| @@ -2043,25 +2067,27 @@ void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, b | |||
| 2043 | const float left_trigger_x = left_joycon_trigger[point * 2 + 0]; | 2067 | const float left_trigger_x = left_joycon_trigger[point * 2 + 0]; |
| 2044 | const float left_trigger_y = left_joycon_trigger[point * 2 + 1]; | 2068 | const float left_trigger_y = left_joycon_trigger[point * 2 + 1]; |
| 2045 | 2069 | ||
| 2046 | qleft_trigger[point] = center + QPointF(left_trigger_x * size + offset, | 2070 | qleft_trigger[point] = |
| 2047 | left_trigger_y * size + (left_pressed ? 0.5f : 0)); | 2071 | center + QPointF(left_trigger_x * size + offset, |
| 2072 | left_trigger_y * size + (left_pressed.value ? 0.5f : 0)); | ||
| 2048 | qright_trigger[point] = | 2073 | qright_trigger[point] = |
| 2049 | center + QPointF(-left_trigger_x * size - offset, | 2074 | center + QPointF(-left_trigger_x * size - offset, |
| 2050 | left_trigger_y * size + (right_pressed ? 0.5f : 0)); | 2075 | left_trigger_y * size + (right_pressed.value ? 0.5f : 0)); |
| 2051 | } | 2076 | } |
| 2052 | 2077 | ||
| 2053 | // Left trigger | 2078 | // Left trigger |
| 2054 | p.setPen(colors.outline); | 2079 | p.setPen(colors.outline); |
| 2055 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2080 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 2056 | DrawPolygon(p, qleft_trigger); | 2081 | DrawPolygon(p, qleft_trigger); |
| 2057 | 2082 | ||
| 2058 | // Right trigger | 2083 | // Right trigger |
| 2059 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2084 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 2060 | DrawPolygon(p, qright_trigger); | 2085 | DrawPolygon(p, qright_trigger); |
| 2061 | } | 2086 | } |
| 2062 | 2087 | ||
| 2063 | void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF center, | 2088 | void PlayerControlPreview::DrawDualTriggersTopView( |
| 2064 | bool left_pressed, bool right_pressed) { | 2089 | QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed, |
| 2090 | const Common::Input::ButtonStatus& right_pressed) { | ||
| 2065 | std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; | 2091 | std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; |
| 2066 | std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; | 2092 | std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; |
| 2067 | constexpr float size = 0.9f; | 2093 | constexpr float size = 0.9f; |
| @@ -2080,9 +2106,9 @@ void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF ce | |||
| 2080 | } | 2106 | } |
| 2081 | 2107 | ||
| 2082 | p.setPen(colors.outline); | 2108 | p.setPen(colors.outline); |
| 2083 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2109 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 2084 | DrawPolygon(p, qleft_trigger); | 2110 | DrawPolygon(p, qleft_trigger); |
| 2085 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2111 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 2086 | DrawPolygon(p, qright_trigger); | 2112 | DrawPolygon(p, qright_trigger); |
| 2087 | 2113 | ||
| 2088 | // Draw L text | 2114 | // Draw L text |
| @@ -2096,8 +2122,9 @@ void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF ce | |||
| 2096 | DrawSymbol(p, center + QPointF(177, -84), Symbol::R, 1.0f); | 2122 | DrawSymbol(p, center + QPointF(177, -84), Symbol::R, 1.0f); |
| 2097 | } | 2123 | } |
| 2098 | 2124 | ||
| 2099 | void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF center, | 2125 | void PlayerControlPreview::DrawDualZTriggersTopView( |
| 2100 | bool left_pressed, bool right_pressed) { | 2126 | QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed, |
| 2127 | const Common::Input::ButtonStatus& right_pressed) { | ||
| 2101 | std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; | 2128 | std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; |
| 2102 | std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; | 2129 | std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; |
| 2103 | constexpr float size = 0.9f; | 2130 | constexpr float size = 0.9f; |
| @@ -2114,9 +2141,9 @@ void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF c | |||
| 2114 | } | 2141 | } |
| 2115 | 2142 | ||
| 2116 | p.setPen(colors.outline); | 2143 | p.setPen(colors.outline); |
| 2117 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2144 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 2118 | DrawPolygon(p, qleft_trigger); | 2145 | DrawPolygon(p, qleft_trigger); |
| 2119 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2146 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 2120 | DrawPolygon(p, qright_trigger); | 2147 | DrawPolygon(p, qright_trigger); |
| 2121 | 2148 | ||
| 2122 | // Draw ZL text | 2149 | // Draw ZL text |
| @@ -2130,7 +2157,8 @@ void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF c | |||
| 2130 | DrawSymbol(p, center + QPointF(180, -113), Symbol::ZR, 1.0f); | 2157 | DrawSymbol(p, center + QPointF(180, -113), Symbol::ZR, 1.0f); |
| 2131 | } | 2158 | } |
| 2132 | 2159 | ||
| 2133 | void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, bool left_pressed) { | 2160 | void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, |
| 2161 | const Common::Input::ButtonStatus& left_pressed) { | ||
| 2134 | std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; | 2162 | std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; |
| 2135 | constexpr float size = 1.78f; | 2163 | constexpr float size = 1.78f; |
| 2136 | constexpr float offset = 311.5f; | 2164 | constexpr float offset = 311.5f; |
| @@ -2138,15 +2166,16 @@ void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, b | |||
| 2138 | for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { | 2166 | for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { |
| 2139 | qleft_trigger[point] = center + QPointF(left_joycon_trigger[point * 2] * size + offset, | 2167 | qleft_trigger[point] = center + QPointF(left_joycon_trigger[point * 2] * size + offset, |
| 2140 | left_joycon_trigger[point * 2 + 1] * size - | 2168 | left_joycon_trigger[point * 2 + 1] * size - |
| 2141 | (left_pressed ? 0.5f : 1.0f)); | 2169 | (left_pressed.value ? 0.5f : 1.0f)); |
| 2142 | } | 2170 | } |
| 2143 | 2171 | ||
| 2144 | p.setPen(colors.outline); | 2172 | p.setPen(colors.outline); |
| 2145 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2173 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 2146 | DrawPolygon(p, qleft_trigger); | 2174 | DrawPolygon(p, qleft_trigger); |
| 2147 | } | 2175 | } |
| 2148 | 2176 | ||
| 2149 | void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center, bool left_pressed) { | 2177 | void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center, |
| 2178 | const Common::Input::ButtonStatus& left_pressed) { | ||
| 2150 | std::array<QPointF, left_joycon_sideview_zl.size() / 2> qleft_trigger; | 2179 | std::array<QPointF, left_joycon_sideview_zl.size() / 2> qleft_trigger; |
| 2151 | constexpr float size = 1.1115f; | 2180 | constexpr float size = 1.1115f; |
| 2152 | constexpr float offset2 = 335; | 2181 | constexpr float offset2 = 335; |
| @@ -2154,18 +2183,18 @@ void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center, | |||
| 2154 | for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { | 2183 | for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { |
| 2155 | qleft_trigger[point] = center + QPointF(left_joycon_sideview_zl[point * 2] * size + offset2, | 2184 | qleft_trigger[point] = center + QPointF(left_joycon_sideview_zl[point * 2] * size + offset2, |
| 2156 | left_joycon_sideview_zl[point * 2 + 1] * size + | 2185 | left_joycon_sideview_zl[point * 2 + 1] * size + |
| 2157 | (left_pressed ? 1.5f : 1.0f)); | 2186 | (left_pressed.value ? 1.5f : 1.0f)); |
| 2158 | } | 2187 | } |
| 2159 | 2188 | ||
| 2160 | p.setPen(colors.outline); | 2189 | p.setPen(colors.outline); |
| 2161 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2190 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 2162 | DrawPolygon(p, qleft_trigger); | 2191 | DrawPolygon(p, qleft_trigger); |
| 2163 | p.drawArc(center.x() + 158, center.y() + (left_pressed ? -203.5f : -204.0f), 77, 77, 225 * 16, | 2192 | p.drawArc(center.x() + 158, center.y() + (left_pressed.value ? -203.5f : -204.0f), 77, 77, |
| 2164 | 44 * 16); | 2193 | 225 * 16, 44 * 16); |
| 2165 | } | 2194 | } |
| 2166 | 2195 | ||
| 2167 | void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF center, | 2196 | void PlayerControlPreview::DrawLeftTriggersTopView( |
| 2168 | bool left_pressed) { | 2197 | QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed) { |
| 2169 | std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; | 2198 | std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; |
| 2170 | 2199 | ||
| 2171 | for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { | 2200 | for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { |
| @@ -2174,7 +2203,7 @@ void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF ce | |||
| 2174 | } | 2203 | } |
| 2175 | 2204 | ||
| 2176 | p.setPen(colors.outline); | 2205 | p.setPen(colors.outline); |
| 2177 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2206 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 2178 | DrawPolygon(p, qleft_trigger); | 2207 | DrawPolygon(p, qleft_trigger); |
| 2179 | 2208 | ||
| 2180 | // Draw L text | 2209 | // Draw L text |
| @@ -2183,8 +2212,8 @@ void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF ce | |||
| 2183 | DrawSymbol(p, center + QPointF(-143, -36), Symbol::L, 1.0f); | 2212 | DrawSymbol(p, center + QPointF(-143, -36), Symbol::L, 1.0f); |
| 2184 | } | 2213 | } |
| 2185 | 2214 | ||
| 2186 | void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF center, | 2215 | void PlayerControlPreview::DrawLeftZTriggersTopView( |
| 2187 | bool left_pressed) { | 2216 | QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed) { |
| 2188 | std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; | 2217 | std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; |
| 2189 | 2218 | ||
| 2190 | for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { | 2219 | for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { |
| @@ -2193,7 +2222,7 @@ void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF c | |||
| 2193 | } | 2222 | } |
| 2194 | 2223 | ||
| 2195 | p.setPen(colors.outline); | 2224 | p.setPen(colors.outline); |
| 2196 | p.setBrush(left_pressed ? colors.highlight : colors.button); | 2225 | p.setBrush(left_pressed.value ? colors.highlight : colors.button); |
| 2197 | DrawPolygon(p, qleft_trigger); | 2226 | DrawPolygon(p, qleft_trigger); |
| 2198 | 2227 | ||
| 2199 | // Draw ZL text | 2228 | // Draw ZL text |
| @@ -2203,7 +2232,7 @@ void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF c | |||
| 2203 | } | 2232 | } |
| 2204 | 2233 | ||
| 2205 | void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center, | 2234 | void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center, |
| 2206 | bool right_pressed) { | 2235 | const Common::Input::ButtonStatus& right_pressed) { |
| 2207 | std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; | 2236 | std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; |
| 2208 | constexpr float size = 1.78f; | 2237 | constexpr float size = 1.78f; |
| 2209 | constexpr float offset = 311.5f; | 2238 | constexpr float offset = 311.5f; |
| @@ -2211,36 +2240,36 @@ void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center, | |||
| 2211 | for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { | 2240 | for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { |
| 2212 | qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset, | 2241 | qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset, |
| 2213 | left_joycon_trigger[point * 2 + 1] * size - | 2242 | left_joycon_trigger[point * 2 + 1] * size - |
| 2214 | (right_pressed ? 0.5f : 1.0f)); | 2243 | (right_pressed.value ? 0.5f : 1.0f)); |
| 2215 | } | 2244 | } |
| 2216 | 2245 | ||
| 2217 | p.setPen(colors.outline); | 2246 | p.setPen(colors.outline); |
| 2218 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2247 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 2219 | DrawPolygon(p, qright_trigger); | 2248 | DrawPolygon(p, qright_trigger); |
| 2220 | } | 2249 | } |
| 2221 | 2250 | ||
| 2222 | void PlayerControlPreview::DrawRightZTriggers(QPainter& p, const QPointF center, | 2251 | void PlayerControlPreview::DrawRightZTriggers(QPainter& p, const QPointF center, |
| 2223 | bool right_pressed) { | 2252 | const Common::Input::ButtonStatus& right_pressed) { |
| 2224 | std::array<QPointF, left_joycon_sideview_zl.size() / 2> qright_trigger; | 2253 | std::array<QPointF, left_joycon_sideview_zl.size() / 2> qright_trigger; |
| 2225 | constexpr float size = 1.1115f; | 2254 | constexpr float size = 1.1115f; |
| 2226 | constexpr float offset2 = 335; | 2255 | constexpr float offset2 = 335; |
| 2227 | 2256 | ||
| 2228 | for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { | 2257 | for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { |
| 2229 | qright_trigger[point] = | 2258 | qright_trigger[point] = |
| 2230 | center + | 2259 | center + QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2, |
| 2231 | QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2, | 2260 | left_joycon_sideview_zl[point * 2 + 1] * size + |
| 2232 | left_joycon_sideview_zl[point * 2 + 1] * size + (right_pressed ? 0.5f : 0) + 1); | 2261 | (right_pressed.value ? 0.5f : 0) + 1); |
| 2233 | } | 2262 | } |
| 2234 | 2263 | ||
| 2235 | p.setPen(colors.outline); | 2264 | p.setPen(colors.outline); |
| 2236 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2265 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 2237 | DrawPolygon(p, qright_trigger); | 2266 | DrawPolygon(p, qright_trigger); |
| 2238 | p.drawArc(center.x() - 236, center.y() + (right_pressed ? -203.5f : -204.0f), 77, 77, 271 * 16, | 2267 | p.drawArc(center.x() - 236, center.y() + (right_pressed.value ? -203.5f : -204.0f), 77, 77, |
| 2239 | 44 * 16); | 2268 | 271 * 16, 44 * 16); |
| 2240 | } | 2269 | } |
| 2241 | 2270 | ||
| 2242 | void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF center, | 2271 | void PlayerControlPreview::DrawRightTriggersTopView( |
| 2243 | bool right_pressed) { | 2272 | QPainter& p, const QPointF center, const Common::Input::ButtonStatus& right_pressed) { |
| 2244 | std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; | 2273 | std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; |
| 2245 | 2274 | ||
| 2246 | for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { | 2275 | for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { |
| @@ -2249,7 +2278,7 @@ void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF c | |||
| 2249 | } | 2278 | } |
| 2250 | 2279 | ||
| 2251 | p.setPen(colors.outline); | 2280 | p.setPen(colors.outline); |
| 2252 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2281 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 2253 | DrawPolygon(p, qright_trigger); | 2282 | DrawPolygon(p, qright_trigger); |
| 2254 | 2283 | ||
| 2255 | // Draw R text | 2284 | // Draw R text |
| @@ -2258,8 +2287,8 @@ void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF c | |||
| 2258 | DrawSymbol(p, center + QPointF(137, -36), Symbol::R, 1.0f); | 2287 | DrawSymbol(p, center + QPointF(137, -36), Symbol::R, 1.0f); |
| 2259 | } | 2288 | } |
| 2260 | 2289 | ||
| 2261 | void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF center, | 2290 | void PlayerControlPreview::DrawRightZTriggersTopView( |
| 2262 | bool right_pressed) { | 2291 | QPainter& p, const QPointF center, const Common::Input::ButtonStatus& right_pressed) { |
| 2263 | std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; | 2292 | std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; |
| 2264 | 2293 | ||
| 2265 | for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { | 2294 | for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { |
| @@ -2268,7 +2297,7 @@ void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF | |||
| 2268 | } | 2297 | } |
| 2269 | 2298 | ||
| 2270 | p.setPen(colors.outline); | 2299 | p.setPen(colors.outline); |
| 2271 | p.setBrush(right_pressed ? colors.highlight : colors.button); | 2300 | p.setBrush(right_pressed.value ? colors.highlight : colors.button); |
| 2272 | DrawPolygon(p, qright_trigger); | 2301 | DrawPolygon(p, qright_trigger); |
| 2273 | 2302 | ||
| 2274 | // Draw ZR text | 2303 | // Draw ZR text |
| @@ -2278,13 +2307,13 @@ void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF | |||
| 2278 | } | 2307 | } |
| 2279 | 2308 | ||
| 2280 | void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float size, | 2309 | void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float size, |
| 2281 | bool pressed) { | 2310 | const Common::Input::ButtonStatus& pressed) { |
| 2282 | const float radius1 = 13.0f * size; | 2311 | const float radius1 = 13.0f * size; |
| 2283 | const float radius2 = 9.0f * size; | 2312 | const float radius2 = 9.0f * size; |
| 2284 | 2313 | ||
| 2285 | // Outer circle | 2314 | // Outer circle |
| 2286 | p.setPen(colors.outline); | 2315 | p.setPen(colors.outline); |
| 2287 | p.setBrush(pressed ? colors.highlight : colors.button); | 2316 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2288 | DrawCircle(p, center, radius1); | 2317 | DrawCircle(p, center, radius1); |
| 2289 | 2318 | ||
| 2290 | // Cross | 2319 | // Cross |
| @@ -2292,17 +2321,18 @@ void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float | |||
| 2292 | p.drawLine(center - QPoint(0, radius1), center + QPoint(0, radius1)); | 2321 | p.drawLine(center - QPoint(0, radius1), center + QPoint(0, radius1)); |
| 2293 | 2322 | ||
| 2294 | // Inner circle | 2323 | // Inner circle |
| 2295 | p.setBrush(pressed ? colors.highlight2 : colors.button2); | 2324 | p.setBrush(pressed.value ? colors.highlight2 : colors.button2); |
| 2296 | DrawCircle(p, center, radius2); | 2325 | DrawCircle(p, center, radius2); |
| 2297 | } | 2326 | } |
| 2298 | 2327 | ||
| 2299 | void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF center, float angle, | 2328 | void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF center, float angle, |
| 2300 | float size, bool pressed) { | 2329 | float size, |
| 2330 | const Common::Input::ButtonStatus& pressed) { | ||
| 2301 | QVector<QPointF> joystick; | 2331 | QVector<QPointF> joystick; |
| 2302 | joystick.reserve(static_cast<int>(left_joystick_sideview.size() / 2)); | 2332 | joystick.reserve(static_cast<int>(left_joystick_sideview.size() / 2)); |
| 2303 | 2333 | ||
| 2304 | for (std::size_t point = 0; point < left_joystick_sideview.size() / 2; ++point) { | 2334 | for (std::size_t point = 0; point < left_joystick_sideview.size() / 2; ++point) { |
| 2305 | joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed ? 1 : 0), | 2335 | joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed.value ? 1 : 0), |
| 2306 | left_joystick_sideview[point * 2 + 1] * size - 1)); | 2336 | left_joystick_sideview[point * 2 + 1] * size - 1)); |
| 2307 | } | 2337 | } |
| 2308 | 2338 | ||
| @@ -2314,14 +2344,15 @@ void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF cente | |||
| 2314 | 2344 | ||
| 2315 | // Draw joystick | 2345 | // Draw joystick |
| 2316 | p.setPen(colors.outline); | 2346 | p.setPen(colors.outline); |
| 2317 | p.setBrush(pressed ? colors.highlight : colors.button); | 2347 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2318 | p.drawPolygon(p2); | 2348 | p.drawPolygon(p2); |
| 2319 | p.drawLine(p2.at(1), p2.at(30)); | 2349 | p.drawLine(p2.at(1), p2.at(30)); |
| 2320 | p.drawLine(p2.at(32), p2.at(71)); | 2350 | p.drawLine(p2.at(32), p2.at(71)); |
| 2321 | } | 2351 | } |
| 2322 | 2352 | ||
| 2323 | void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, const QPointF offset, | 2353 | void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, const QPointF offset, |
| 2324 | float offset_scalar, bool pressed) { | 2354 | float offset_scalar, |
| 2355 | const Common::Input::ButtonStatus& pressed) { | ||
| 2325 | const float radius1 = 24.0f; | 2356 | const float radius1 = 24.0f; |
| 2326 | const float radius2 = 17.0f; | 2357 | const float radius2 = 17.0f; |
| 2327 | 2358 | ||
| @@ -2339,11 +2370,11 @@ void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, co | |||
| 2339 | 2370 | ||
| 2340 | // Outer circle | 2371 | // Outer circle |
| 2341 | p.setPen(colors.outline); | 2372 | p.setPen(colors.outline); |
| 2342 | p.setBrush(pressed ? colors.highlight : colors.button); | 2373 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2343 | p.drawEllipse(QPointF(0, 0), radius1 * amplitude, radius1); | 2374 | p.drawEllipse(QPointF(0, 0), radius1 * amplitude, radius1); |
| 2344 | 2375 | ||
| 2345 | // Inner circle | 2376 | // Inner circle |
| 2346 | p.setBrush(pressed ? colors.highlight2 : colors.button2); | 2377 | p.setBrush(pressed.value ? colors.highlight2 : colors.button2); |
| 2347 | 2378 | ||
| 2348 | const float inner_offset = | 2379 | const float inner_offset = |
| 2349 | (radius1 - radius2) * 0.4f * ((offset.x() == 0 && offset.y() < 0) ? -1.0f : 1.0f); | 2380 | (radius1 - radius2) * 0.4f * ((offset.x() == 0 && offset.y() < 0) ? -1.0f : 1.0f); |
| @@ -2355,14 +2386,15 @@ void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, co | |||
| 2355 | p.restore(); | 2386 | p.restore(); |
| 2356 | } | 2387 | } |
| 2357 | 2388 | ||
| 2358 | void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, bool pressed) { | 2389 | void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, |
| 2390 | const Common::Input::ButtonStatus& pressed) { | ||
| 2359 | // Outer circle | 2391 | // Outer circle |
| 2360 | p.setPen(colors.outline); | 2392 | p.setPen(colors.outline); |
| 2361 | p.setBrush(pressed ? colors.highlight : colors.button); | 2393 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2362 | DrawCircle(p, center, 26.0f); | 2394 | DrawCircle(p, center, 26.0f); |
| 2363 | 2395 | ||
| 2364 | // Inner circle | 2396 | // Inner circle |
| 2365 | p.setBrush(pressed ? colors.highlight2 : colors.button2); | 2397 | p.setBrush(pressed.value ? colors.highlight2 : colors.button2); |
| 2366 | DrawCircle(p, center, 19.0f); | 2398 | DrawCircle(p, center, 19.0f); |
| 2367 | p.setBrush(colors.transparent); | 2399 | p.setBrush(colors.transparent); |
| 2368 | DrawCircle(p, center, 13.5f); | 2400 | DrawCircle(p, center, 13.5f); |
| @@ -2371,31 +2403,29 @@ void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, boo | |||
| 2371 | 2403 | ||
| 2372 | void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) { | 2404 | void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) { |
| 2373 | using namespace Settings::NativeAnalog; | 2405 | using namespace Settings::NativeAnalog; |
| 2374 | if (controller_type != Settings::ControllerType::LeftJoycon) { | 2406 | if (center_right != QPointF(0, 0)) { |
| 2375 | DrawJoystickProperties(p, center_right, axis_values[RStick].properties); | 2407 | DrawJoystickProperties(p, center_right, stick_values[RStick].x.properties); |
| 2376 | p.setPen(colors.indicator); | 2408 | p.setPen(colors.indicator); |
| 2377 | p.setBrush(colors.indicator); | 2409 | p.setBrush(colors.indicator); |
| 2378 | DrawJoystickDot(p, center_right, axis_values[RStick].raw_value, | 2410 | DrawJoystickDot(p, center_right, stick_values[RStick], true); |
| 2379 | axis_values[RStick].properties); | ||
| 2380 | p.setPen(colors.indicator2); | 2411 | p.setPen(colors.indicator2); |
| 2381 | p.setBrush(colors.indicator2); | 2412 | p.setBrush(colors.indicator2); |
| 2382 | DrawJoystickDot(p, center_right, axis_values[RStick].value, axis_values[RStick].properties); | 2413 | DrawJoystickDot(p, center_right, stick_values[RStick], false); |
| 2383 | } | 2414 | } |
| 2384 | 2415 | ||
| 2385 | if (controller_type != Settings::ControllerType::RightJoycon) { | 2416 | if (center_left != QPointF(0, 0)) { |
| 2386 | DrawJoystickProperties(p, center_left, axis_values[LStick].properties); | 2417 | DrawJoystickProperties(p, center_left, stick_values[LStick].x.properties); |
| 2387 | p.setPen(colors.indicator); | 2418 | p.setPen(colors.indicator); |
| 2388 | p.setBrush(colors.indicator); | 2419 | p.setBrush(colors.indicator); |
| 2389 | DrawJoystickDot(p, center_left, axis_values[LStick].raw_value, | 2420 | DrawJoystickDot(p, center_left, stick_values[LStick], true); |
| 2390 | axis_values[LStick].properties); | ||
| 2391 | p.setPen(colors.indicator2); | 2421 | p.setPen(colors.indicator2); |
| 2392 | p.setBrush(colors.indicator2); | 2422 | p.setBrush(colors.indicator2); |
| 2393 | DrawJoystickDot(p, center_left, axis_values[LStick].value, axis_values[LStick].properties); | 2423 | DrawJoystickDot(p, center_left, stick_values[LStick], false); |
| 2394 | } | 2424 | } |
| 2395 | } | 2425 | } |
| 2396 | 2426 | ||
| 2397 | void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF center, | 2427 | void PlayerControlPreview::DrawJoystickProperties( |
| 2398 | const Input::AnalogProperties& properties) { | 2428 | QPainter& p, const QPointF center, const Common::Input::AnalogProperties& properties) { |
| 2399 | constexpr float size = 45.0f; | 2429 | constexpr float size = 45.0f; |
| 2400 | const float range = size * properties.range; | 2430 | const float range = size * properties.range; |
| 2401 | const float deadzone = size * properties.deadzone; | 2431 | const float deadzone = size * properties.deadzone; |
| @@ -2414,19 +2444,26 @@ void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF cen | |||
| 2414 | DrawCircle(p, center, deadzone); | 2444 | DrawCircle(p, center, deadzone); |
| 2415 | } | 2445 | } |
| 2416 | 2446 | ||
| 2417 | void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, const QPointF value, | 2447 | void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, |
| 2418 | const Input::AnalogProperties& properties) { | 2448 | const Common::Input::StickStatus& stick, bool raw) { |
| 2419 | constexpr float size = 45.0f; | 2449 | constexpr float size = 45.0f; |
| 2420 | const float range = size * properties.range; | 2450 | const float range = size * stick.x.properties.range; |
| 2421 | 2451 | ||
| 2422 | // Dot pointer | 2452 | if (raw) { |
| 2423 | DrawCircle(p, center + (value * range), 2); | 2453 | const QPointF value = QPointF(stick.x.raw_value, stick.y.raw_value) * size; |
| 2454 | DrawCircle(p, center + value, 2); | ||
| 2455 | return; | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | const QPointF value = QPointF(stick.x.value, stick.y.value) * range; | ||
| 2459 | DrawCircle(p, center + value, 2); | ||
| 2424 | } | 2460 | } |
| 2425 | 2461 | ||
| 2426 | void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, | 2462 | void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, |
| 2463 | const Common::Input::ButtonStatus& pressed, float width, | ||
| 2427 | float height, Direction direction, float radius) { | 2464 | float height, Direction direction, float radius) { |
| 2428 | p.setBrush(button_color); | 2465 | p.setBrush(button_color); |
| 2429 | if (pressed) { | 2466 | if (pressed.value) { |
| 2430 | switch (direction) { | 2467 | switch (direction) { |
| 2431 | case Direction::Left: | 2468 | case Direction::Left: |
| 2432 | center.setX(center.x() - 1); | 2469 | center.setX(center.x() - 1); |
| @@ -2448,17 +2485,19 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, bool pre | |||
| 2448 | QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; | 2485 | QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; |
| 2449 | p.drawRoundedRect(rect, radius, radius); | 2486 | p.drawRoundedRect(rect, radius, radius); |
| 2450 | } | 2487 | } |
| 2451 | void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, bool pressed, | 2488 | void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, |
| 2489 | const Common::Input::ButtonStatus& pressed, | ||
| 2452 | int button_size) { | 2490 | int button_size) { |
| 2453 | p.setPen(colors.outline); | 2491 | p.setPen(colors.outline); |
| 2454 | p.setBrush(pressed ? colors.highlight : colors.button); | 2492 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2455 | DrawRectangle(p, center, button_size, button_size / 3.0f); | 2493 | DrawRectangle(p, center, button_size, button_size / 3.0f); |
| 2456 | } | 2494 | } |
| 2457 | void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, bool pressed, | 2495 | void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, |
| 2496 | const Common::Input::ButtonStatus& pressed, | ||
| 2458 | int button_size) { | 2497 | int button_size) { |
| 2459 | // Draw outer line | 2498 | // Draw outer line |
| 2460 | p.setPen(colors.outline); | 2499 | p.setPen(colors.outline); |
| 2461 | p.setBrush(pressed ? colors.highlight : colors.button); | 2500 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2462 | DrawRectangle(p, center, button_size, button_size / 3.0f); | 2501 | DrawRectangle(p, center, button_size, button_size / 3.0f); |
| 2463 | DrawRectangle(p, center, button_size / 3.0f, button_size); | 2502 | DrawRectangle(p, center, button_size / 3.0f, button_size); |
| 2464 | 2503 | ||
| @@ -2471,7 +2510,8 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, boo | |||
| 2471 | DrawRectangle(p, center, button_size / 3.0f, button_size); | 2510 | DrawRectangle(p, center, button_size / 3.0f, button_size); |
| 2472 | } | 2511 | } |
| 2473 | 2512 | ||
| 2474 | void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, bool pressed) { | 2513 | void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, |
| 2514 | const Common::Input::ButtonStatus& pressed) { | ||
| 2475 | std::array<QPointF, gc_button_x.size() / 2> button_x; | 2515 | std::array<QPointF, gc_button_x.size() / 2> button_x; |
| 2476 | 2516 | ||
| 2477 | for (std::size_t point = 0; point < gc_button_x.size() / 2; ++point) { | 2517 | for (std::size_t point = 0; point < gc_button_x.size() / 2; ++point) { |
| @@ -2479,11 +2519,12 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, bool | |||
| 2479 | } | 2519 | } |
| 2480 | 2520 | ||
| 2481 | p.setPen(colors.outline); | 2521 | p.setPen(colors.outline); |
| 2482 | p.setBrush(pressed ? colors.highlight : colors.button); | 2522 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2483 | DrawPolygon(p, button_x); | 2523 | DrawPolygon(p, button_x); |
| 2484 | } | 2524 | } |
| 2485 | 2525 | ||
| 2486 | void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, bool pressed) { | 2526 | void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, |
| 2527 | const Common::Input::ButtonStatus& pressed) { | ||
| 2487 | std::array<QPointF, gc_button_y.size() / 2> button_x; | 2528 | std::array<QPointF, gc_button_y.size() / 2> button_x; |
| 2488 | 2529 | ||
| 2489 | for (std::size_t point = 0; point < gc_button_y.size() / 2; ++point) { | 2530 | for (std::size_t point = 0; point < gc_button_y.size() / 2; ++point) { |
| @@ -2491,27 +2532,29 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, bool | |||
| 2491 | } | 2532 | } |
| 2492 | 2533 | ||
| 2493 | p.setPen(colors.outline); | 2534 | p.setPen(colors.outline); |
| 2494 | p.setBrush(pressed ? colors.highlight : colors.button); | 2535 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2495 | DrawPolygon(p, button_x); | 2536 | DrawPolygon(p, button_x); |
| 2496 | } | 2537 | } |
| 2497 | 2538 | ||
| 2498 | void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center, bool pressed) { | 2539 | void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center, |
| 2540 | const Common::Input::ButtonStatus& pressed) { | ||
| 2499 | std::array<QPointF, gc_button_z.size() / 2> button_x; | 2541 | std::array<QPointF, gc_button_z.size() / 2> button_x; |
| 2500 | 2542 | ||
| 2501 | for (std::size_t point = 0; point < gc_button_z.size() / 2; ++point) { | 2543 | for (std::size_t point = 0; point < gc_button_z.size() / 2; ++point) { |
| 2502 | button_x[point] = center + QPointF(gc_button_z[point * 2], | 2544 | button_x[point] = center + QPointF(gc_button_z[point * 2], |
| 2503 | gc_button_z[point * 2 + 1] + (pressed ? 1 : 0)); | 2545 | gc_button_z[point * 2 + 1] + (pressed.value ? 1 : 0)); |
| 2504 | } | 2546 | } |
| 2505 | 2547 | ||
| 2506 | p.setPen(colors.outline); | 2548 | p.setPen(colors.outline); |
| 2507 | p.setBrush(pressed ? colors.highlight : colors.button2); | 2549 | p.setBrush(pressed.value ? colors.highlight : colors.button2); |
| 2508 | DrawPolygon(p, button_x); | 2550 | DrawPolygon(p, button_x); |
| 2509 | } | 2551 | } |
| 2510 | 2552 | ||
| 2511 | void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, bool pressed, | 2553 | void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, |
| 2554 | const Common::Input::ButtonStatus& pressed, | ||
| 2512 | float button_size) { | 2555 | float button_size) { |
| 2513 | p.setBrush(button_color); | 2556 | p.setBrush(button_color); |
| 2514 | if (pressed) { | 2557 | if (pressed.value) { |
| 2515 | p.setBrush(colors.highlight); | 2558 | p.setBrush(colors.highlight); |
| 2516 | } | 2559 | } |
| 2517 | p.drawEllipse(center, button_size, button_size); | 2560 | p.drawEllipse(center, button_size, button_size); |
| @@ -2540,7 +2583,8 @@ void PlayerControlPreview::DrawArrowButtonOutline(QPainter& p, const QPointF cen | |||
| 2540 | } | 2583 | } |
| 2541 | 2584 | ||
| 2542 | void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center, | 2585 | void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center, |
| 2543 | const Direction direction, bool pressed, float size) { | 2586 | const Direction direction, |
| 2587 | const Common::Input::ButtonStatus& pressed, float size) { | ||
| 2544 | std::array<QPointF, up_arrow_button.size() / 2> arrow_button; | 2588 | std::array<QPointF, up_arrow_button.size() / 2> arrow_button; |
| 2545 | QPoint offset; | 2589 | QPoint offset; |
| 2546 | 2590 | ||
| @@ -2552,38 +2596,39 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center, | |||
| 2552 | case Direction::Up: | 2596 | case Direction::Up: |
| 2553 | arrow_button[point] = center + QPointF(up_arrow_x * size, up_arrow_y * size); | 2597 | arrow_button[point] = center + QPointF(up_arrow_x * size, up_arrow_y * size); |
| 2554 | break; | 2598 | break; |
| 2555 | case Direction::Left: | ||
| 2556 | arrow_button[point] = center + QPointF(up_arrow_y * size, up_arrow_x * size); | ||
| 2557 | break; | ||
| 2558 | case Direction::Right: | 2599 | case Direction::Right: |
| 2559 | arrow_button[point] = center + QPointF(-up_arrow_y * size, up_arrow_x * size); | 2600 | arrow_button[point] = center + QPointF(-up_arrow_y * size, up_arrow_x * size); |
| 2560 | break; | 2601 | break; |
| 2561 | case Direction::Down: | 2602 | case Direction::Down: |
| 2562 | arrow_button[point] = center + QPointF(up_arrow_x * size, -up_arrow_y * size); | 2603 | arrow_button[point] = center + QPointF(up_arrow_x * size, -up_arrow_y * size); |
| 2563 | break; | 2604 | break; |
| 2605 | case Direction::Left: | ||
| 2606 | // Compiler doesn't optimize this correctly check why | ||
| 2607 | arrow_button[point] = center + QPointF(up_arrow_y * size, up_arrow_x * size); | ||
| 2608 | break; | ||
| 2564 | case Direction::None: | 2609 | case Direction::None: |
| 2565 | break; | 2610 | break; |
| 2566 | } | 2611 | } |
| 2567 | } | 2612 | } |
| 2568 | 2613 | ||
| 2569 | // Draw arrow button | 2614 | // Draw arrow button |
| 2570 | p.setPen(pressed ? colors.highlight : colors.button); | 2615 | p.setPen(pressed.value ? colors.highlight : colors.button); |
| 2571 | p.setBrush(pressed ? colors.highlight : colors.button); | 2616 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2572 | DrawPolygon(p, arrow_button); | 2617 | DrawPolygon(p, arrow_button); |
| 2573 | 2618 | ||
| 2574 | switch (direction) { | 2619 | switch (direction) { |
| 2575 | case Direction::Up: | 2620 | case Direction::Up: |
| 2576 | offset = QPoint(0, -20 * size); | 2621 | offset = QPoint(0, -20 * size); |
| 2577 | break; | 2622 | break; |
| 2578 | case Direction::Left: | ||
| 2579 | offset = QPoint(-20 * size, 0); | ||
| 2580 | break; | ||
| 2581 | case Direction::Right: | 2623 | case Direction::Right: |
| 2582 | offset = QPoint(20 * size, 0); | 2624 | offset = QPoint(20 * size, 0); |
| 2583 | break; | 2625 | break; |
| 2584 | case Direction::Down: | 2626 | case Direction::Down: |
| 2585 | offset = QPoint(0, 20 * size); | 2627 | offset = QPoint(0, 20 * size); |
| 2586 | break; | 2628 | break; |
| 2629 | case Direction::Left: | ||
| 2630 | offset = QPoint(-20 * size, 0); | ||
| 2631 | break; | ||
| 2587 | case Direction::None: | 2632 | case Direction::None: |
| 2588 | offset = QPoint(0, 0); | 2633 | offset = QPoint(0, 0); |
| 2589 | break; | 2634 | break; |
| @@ -2596,7 +2641,8 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center, | |||
| 2596 | } | 2641 | } |
| 2597 | 2642 | ||
| 2598 | void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center, | 2643 | void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center, |
| 2599 | const Direction direction, bool pressed) { | 2644 | const Direction direction, |
| 2645 | const Common::Input::ButtonStatus& pressed) { | ||
| 2600 | std::array<QPointF, trigger_button.size() / 2> qtrigger_button; | 2646 | std::array<QPointF, trigger_button.size() / 2> qtrigger_button; |
| 2601 | 2647 | ||
| 2602 | for (std::size_t point = 0; point < trigger_button.size() / 2; ++point) { | 2648 | for (std::size_t point = 0; point < trigger_button.size() / 2; ++point) { |
| @@ -2619,10 +2665,51 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center, | |||
| 2619 | 2665 | ||
| 2620 | // Draw arrow button | 2666 | // Draw arrow button |
| 2621 | p.setPen(colors.outline); | 2667 | p.setPen(colors.outline); |
| 2622 | p.setBrush(pressed ? colors.highlight : colors.button); | 2668 | p.setBrush(pressed.value ? colors.highlight : colors.button); |
| 2623 | DrawPolygon(p, qtrigger_button); | 2669 | DrawPolygon(p, qtrigger_button); |
| 2624 | } | 2670 | } |
| 2625 | 2671 | ||
| 2672 | void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center, | ||
| 2673 | Common::Input::BatteryLevel battery) { | ||
| 2674 | if (battery == Common::Input::BatteryLevel::None) { | ||
| 2675 | return; | ||
| 2676 | } | ||
| 2677 | p.setPen(colors.outline); | ||
| 2678 | p.setBrush(colors.transparent); | ||
| 2679 | p.drawRect(center.x(), center.y(), 56, 20); | ||
| 2680 | p.drawRect(center.x() + 56, center.y() + 6, 3, 8); | ||
| 2681 | p.setBrush(colors.deadzone); | ||
| 2682 | switch (battery) { | ||
| 2683 | case Common::Input::BatteryLevel::Charging: | ||
| 2684 | p.setBrush(colors.indicator2); | ||
| 2685 | p.drawText(center + QPoint(2, 14), tr("Charging")); | ||
| 2686 | break; | ||
| 2687 | case Common::Input::BatteryLevel::Full: | ||
| 2688 | p.drawRect(center.x() + 42, center.y(), 14, 20); | ||
| 2689 | p.drawRect(center.x() + 28, center.y(), 14, 20); | ||
| 2690 | p.drawRect(center.x() + 14, center.y(), 14, 20); | ||
| 2691 | p.drawRect(center.x(), center.y(), 14, 20); | ||
| 2692 | break; | ||
| 2693 | case Common::Input::BatteryLevel::Medium: | ||
| 2694 | p.drawRect(center.x() + 28, center.y(), 14, 20); | ||
| 2695 | p.drawRect(center.x() + 14, center.y(), 14, 20); | ||
| 2696 | p.drawRect(center.x(), center.y(), 14, 20); | ||
| 2697 | break; | ||
| 2698 | case Common::Input::BatteryLevel::Low: | ||
| 2699 | p.drawRect(center.x() + 14, center.y(), 14, 20); | ||
| 2700 | p.drawRect(center.x(), center.y(), 14, 20); | ||
| 2701 | break; | ||
| 2702 | case Common::Input::BatteryLevel::Critical: | ||
| 2703 | p.drawRect(center.x(), center.y(), 14, 20); | ||
| 2704 | break; | ||
| 2705 | case Common::Input::BatteryLevel::Empty: | ||
| 2706 | p.drawRect(center.x(), center.y(), 5, 20); | ||
| 2707 | break; | ||
| 2708 | default: | ||
| 2709 | break; | ||
| 2710 | } | ||
| 2711 | } | ||
| 2712 | |||
| 2626 | void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol symbol, | 2713 | void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol symbol, |
| 2627 | float icon_size) { | 2714 | float icon_size) { |
| 2628 | std::array<QPointF, house.size() / 2> house_icon; | 2715 | std::array<QPointF, house.size() / 2> house_icon; |
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h index f4bbfa528..4cd5c3be0 100644 --- a/src/yuzu/configuration/configure_input_player_widget.h +++ b/src/yuzu/configuration/configure_input_player_widget.h | |||
| @@ -7,9 +7,11 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <QFrame> | 8 | #include <QFrame> |
| 9 | #include <QPointer> | 9 | #include <QPointer> |
| 10 | #include "common/settings.h" | 10 | |
| 11 | #include "core/frontend/input.h" | 11 | #include "common/input.h" |
| 12 | #include "yuzu/debugger/controller.h" | 12 | #include "common/settings_input.h" |
| 13 | #include "core/hid/emulated_controller.h" | ||
| 14 | #include "core/hid/hid_types.h" | ||
| 13 | 15 | ||
| 14 | class QLabel; | 16 | class QLabel; |
| 15 | 17 | ||
| @@ -24,17 +26,26 @@ public: | |||
| 24 | explicit PlayerControlPreview(QWidget* parent); | 26 | explicit PlayerControlPreview(QWidget* parent); |
| 25 | ~PlayerControlPreview() override; | 27 | ~PlayerControlPreview() override; |
| 26 | 28 | ||
| 27 | void SetPlayerInput(std::size_t index, const ButtonParam& buttons_param, | 29 | // Sets the emulated controller to be displayed |
| 28 | const AnalogParam& analogs_param); | 30 | void SetController(Core::HID::EmulatedController* controller); |
| 29 | void SetPlayerInputRaw(std::size_t index, const Settings::ButtonsRaw& buttons_, | 31 | |
| 30 | Settings::AnalogsRaw analogs_); | 32 | // Disables events from the emulated controller |
| 31 | void SetConnectedStatus(bool checked); | 33 | void UnloadController(); |
| 32 | void SetControllerType(Settings::ControllerType type); | 34 | |
| 35 | // Starts blinking animation at the button specified | ||
| 33 | void BeginMappingButton(std::size_t button_id); | 36 | void BeginMappingButton(std::size_t button_id); |
| 34 | void BeginMappingAnalog(std::size_t button_id); | 37 | |
| 38 | // Starts moving animation at the stick specified | ||
| 39 | void BeginMappingAnalog(std::size_t stick_id); | ||
| 40 | |||
| 41 | // Stops any ongoing animation | ||
| 35 | void EndMapping(); | 42 | void EndMapping(); |
| 43 | |||
| 44 | // Handles emulated controller events | ||
| 45 | void ControllerUpdate(Core::HID::ControllerTriggerType type); | ||
| 46 | |||
| 47 | // Updates input on sheduled interval | ||
| 36 | void UpdateInput(); | 48 | void UpdateInput(); |
| 37 | void SetCallBack(ControllerCallback callback_); | ||
| 38 | 49 | ||
| 39 | protected: | 50 | protected: |
| 40 | void paintEvent(QPaintEvent* event) override; | 51 | void paintEvent(QPaintEvent* event) override; |
| @@ -63,22 +74,6 @@ private: | |||
| 63 | SR, | 74 | SR, |
| 64 | }; | 75 | }; |
| 65 | 76 | ||
| 66 | struct AxisValue { | ||
| 67 | QPointF value{}; | ||
| 68 | QPointF raw_value{}; | ||
| 69 | Input::AnalogProperties properties{}; | ||
| 70 | int size{}; | ||
| 71 | QPoint offset{}; | ||
| 72 | bool active{}; | ||
| 73 | }; | ||
| 74 | |||
| 75 | struct LedPattern { | ||
| 76 | bool position1; | ||
| 77 | bool position2; | ||
| 78 | bool position3; | ||
| 79 | bool position4; | ||
| 80 | }; | ||
| 81 | |||
| 82 | struct ColorMapping { | 77 | struct ColorMapping { |
| 83 | QColor outline{}; | 78 | QColor outline{}; |
| 84 | QColor primary{}; | 79 | QColor primary{}; |
| @@ -101,7 +96,6 @@ private: | |||
| 101 | QColor deadzone{}; | 96 | QColor deadzone{}; |
| 102 | }; | 97 | }; |
| 103 | 98 | ||
| 104 | static LedPattern GetColorPattern(std::size_t index, bool player_on); | ||
| 105 | void UpdateColors(); | 99 | void UpdateColors(); |
| 106 | void ResetInputs(); | 100 | void ResetInputs(); |
| 107 | 101 | ||
| @@ -122,47 +116,75 @@ private: | |||
| 122 | void DrawGCBody(QPainter& p, QPointF center); | 116 | void DrawGCBody(QPainter& p, QPointF center); |
| 123 | 117 | ||
| 124 | // Draw triggers functions | 118 | // Draw triggers functions |
| 125 | void DrawProTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); | 119 | void DrawProTriggers(QPainter& p, QPointF center, |
| 126 | void DrawGCTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); | 120 | const Common::Input::ButtonStatus& left_pressed, |
| 127 | void DrawHandheldTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); | 121 | const Common::Input::ButtonStatus& right_pressed); |
| 128 | void DrawDualTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); | 122 | void DrawGCTriggers(QPainter& p, QPointF center, Common::Input::TriggerStatus left_trigger, |
| 129 | void DrawDualTriggersTopView(QPainter& p, QPointF center, bool left_pressed, | 123 | Common::Input::TriggerStatus right_trigger); |
| 130 | bool right_pressed); | 124 | void DrawHandheldTriggers(QPainter& p, QPointF center, |
| 131 | void DrawDualZTriggersTopView(QPainter& p, QPointF center, bool left_pressed, | 125 | const Common::Input::ButtonStatus& left_pressed, |
| 132 | bool right_pressed); | 126 | const Common::Input::ButtonStatus& right_pressed); |
| 133 | void DrawLeftTriggers(QPainter& p, QPointF center, bool left_pressed); | 127 | void DrawDualTriggers(QPainter& p, QPointF center, |
| 134 | void DrawLeftZTriggers(QPainter& p, QPointF center, bool left_pressed); | 128 | const Common::Input::ButtonStatus& left_pressed, |
| 135 | void DrawLeftTriggersTopView(QPainter& p, QPointF center, bool left_pressed); | 129 | const Common::Input::ButtonStatus& right_pressed); |
| 136 | void DrawLeftZTriggersTopView(QPainter& p, QPointF center, bool left_pressed); | 130 | void DrawDualTriggersTopView(QPainter& p, QPointF center, |
| 137 | void DrawRightTriggers(QPainter& p, QPointF center, bool right_pressed); | 131 | const Common::Input::ButtonStatus& left_pressed, |
| 138 | void DrawRightZTriggers(QPainter& p, QPointF center, bool right_pressed); | 132 | const Common::Input::ButtonStatus& right_pressed); |
| 139 | void DrawRightTriggersTopView(QPainter& p, QPointF center, bool right_pressed); | 133 | void DrawDualZTriggersTopView(QPainter& p, QPointF center, |
| 140 | void DrawRightZTriggersTopView(QPainter& p, QPointF center, bool right_pressed); | 134 | const Common::Input::ButtonStatus& left_pressed, |
| 135 | const Common::Input::ButtonStatus& right_pressed); | ||
| 136 | void DrawLeftTriggers(QPainter& p, QPointF center, | ||
| 137 | const Common::Input::ButtonStatus& left_pressed); | ||
| 138 | void DrawLeftZTriggers(QPainter& p, QPointF center, | ||
| 139 | const Common::Input::ButtonStatus& left_pressed); | ||
| 140 | void DrawLeftTriggersTopView(QPainter& p, QPointF center, | ||
| 141 | const Common::Input::ButtonStatus& left_pressed); | ||
| 142 | void DrawLeftZTriggersTopView(QPainter& p, QPointF center, | ||
| 143 | const Common::Input::ButtonStatus& left_pressed); | ||
| 144 | void DrawRightTriggers(QPainter& p, QPointF center, | ||
| 145 | const Common::Input::ButtonStatus& right_pressed); | ||
| 146 | void DrawRightZTriggers(QPainter& p, QPointF center, | ||
| 147 | const Common::Input::ButtonStatus& right_pressed); | ||
| 148 | void DrawRightTriggersTopView(QPainter& p, QPointF center, | ||
| 149 | const Common::Input::ButtonStatus& right_pressed); | ||
| 150 | void DrawRightZTriggersTopView(QPainter& p, QPointF center, | ||
| 151 | const Common::Input::ButtonStatus& right_pressed); | ||
| 141 | 152 | ||
| 142 | // Draw joystick functions | 153 | // Draw joystick functions |
| 143 | void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed); | 154 | void DrawJoystick(QPainter& p, QPointF center, float size, |
| 144 | void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed); | 155 | const Common::Input::ButtonStatus& pressed); |
| 156 | void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, | ||
| 157 | const Common::Input::ButtonStatus& pressed); | ||
| 145 | void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right); | 158 | void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right); |
| 146 | void DrawJoystickProperties(QPainter& p, QPointF center, | 159 | void DrawJoystickProperties(QPainter& p, QPointF center, |
| 147 | const Input::AnalogProperties& properties); | 160 | const Common::Input::AnalogProperties& properties); |
| 148 | void DrawJoystickDot(QPainter& p, QPointF center, QPointF value, | 161 | void DrawJoystickDot(QPainter& p, QPointF center, const Common::Input::StickStatus& stick, |
| 149 | const Input::AnalogProperties& properties); | 162 | bool raw); |
| 150 | void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar, bool pressed); | 163 | void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar, |
| 151 | void DrawGCJoystick(QPainter& p, QPointF center, bool pressed); | 164 | const Common::Input::ButtonStatus& pressed); |
| 165 | void DrawGCJoystick(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed); | ||
| 152 | 166 | ||
| 153 | // Draw button functions | 167 | // Draw button functions |
| 154 | void DrawCircleButton(QPainter& p, QPointF center, bool pressed, float button_size); | 168 | void DrawCircleButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed, |
| 155 | void DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, float height, | 169 | float button_size); |
| 156 | Direction direction = Direction::None, float radius = 2); | 170 | void DrawRoundButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed, |
| 157 | void DrawMinusButton(QPainter& p, QPointF center, bool pressed, int button_size); | 171 | float width, float height, Direction direction = Direction::None, |
| 158 | void DrawPlusButton(QPainter& p, QPointF center, bool pressed, int button_size); | 172 | float radius = 2); |
| 159 | void DrawGCButtonX(QPainter& p, QPointF center, bool pressed); | 173 | void DrawMinusButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed, |
| 160 | void DrawGCButtonY(QPainter& p, QPointF center, bool pressed); | 174 | int button_size); |
| 161 | void DrawGCButtonZ(QPainter& p, QPointF center, bool pressed); | 175 | void DrawPlusButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed, |
| 176 | int button_size); | ||
| 177 | void DrawGCButtonX(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed); | ||
| 178 | void DrawGCButtonY(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed); | ||
| 179 | void DrawGCButtonZ(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed); | ||
| 162 | void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f); | 180 | void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f); |
| 163 | void DrawArrowButton(QPainter& p, QPointF center, Direction direction, bool pressed, | 181 | void DrawArrowButton(QPainter& p, QPointF center, Direction direction, |
| 164 | float size = 1.0f); | 182 | const Common::Input::ButtonStatus& pressed, float size = 1.0f); |
| 165 | void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, bool pressed); | 183 | void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, |
| 184 | const Common::Input::ButtonStatus& pressed); | ||
| 185 | |||
| 186 | // Draw battery functions | ||
| 187 | void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery); | ||
| 166 | 188 | ||
| 167 | // Draw icon functions | 189 | // Draw icon functions |
| 168 | void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size); | 190 | void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size); |
| @@ -178,24 +200,23 @@ private: | |||
| 178 | void SetTextFont(QPainter& p, float text_size, | 200 | void SetTextFont(QPainter& p, float text_size, |
| 179 | const QString& font_family = QStringLiteral("sans-serif")); | 201 | const QString& font_family = QStringLiteral("sans-serif")); |
| 180 | 202 | ||
| 181 | using ButtonArray = | 203 | bool is_controller_set{}; |
| 182 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::BUTTON_NS_END>; | 204 | bool is_connected{}; |
| 183 | using StickArray = | 205 | bool needs_redraw{}; |
| 184 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>; | 206 | Core::HID::NpadStyleIndex controller_type; |
| 185 | 207 | ||
| 186 | ControllerCallback controller_callback; | ||
| 187 | bool is_enabled{}; | ||
| 188 | bool mapping_active{}; | 208 | bool mapping_active{}; |
| 189 | int blink_counter{}; | 209 | int blink_counter{}; |
| 210 | int callback_key; | ||
| 190 | QColor button_color{}; | 211 | QColor button_color{}; |
| 191 | ColorMapping colors{}; | 212 | ColorMapping colors{}; |
| 192 | std::array<QColor, 4> led_color{}; | 213 | Core::HID::LedPattern led_pattern{0, 0, 0, 0}; |
| 193 | ButtonArray buttons{}; | ||
| 194 | StickArray sticks{}; | ||
| 195 | std::size_t player_index{}; | 214 | std::size_t player_index{}; |
| 196 | std::size_t button_mapping_index{Settings::NativeButton::BUTTON_NS_END}; | 215 | Core::HID::EmulatedController* controller; |
| 197 | std::size_t analog_mapping_index{Settings::NativeAnalog::NUM_STICKS_HID}; | 216 | std::size_t button_mapping_index{Settings::NativeButton::NumButtons}; |
| 198 | std::array<AxisValue, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{}; | 217 | std::size_t analog_mapping_index{Settings::NativeAnalog::NumAnalogs}; |
| 199 | std::array<bool, Settings::NativeButton::NumButtons> button_values{}; | 218 | Core::HID::ButtonValues button_values{}; |
| 200 | Settings::ControllerType controller_type{Settings::ControllerType::ProController}; | 219 | Core::HID::SticksValues stick_values{}; |
| 220 | Core::HID::TriggerValues trigger_values{}; | ||
| 221 | Core::HID::BatteryValues battery_values{}; | ||
| 201 | }; | 222 | }; |
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp index cd5a88cea..17bbe6b61 100644 --- a/src/yuzu/configuration/configure_input_profile_dialog.cpp +++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp | |||
| @@ -11,8 +11,8 @@ ConfigureInputProfileDialog::ConfigureInputProfileDialog( | |||
| 11 | QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles, | 11 | QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles, |
| 12 | Core::System& system) | 12 | Core::System& system) |
| 13 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()), | 13 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()), |
| 14 | profile_widget( | 14 | profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, |
| 15 | new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, system, false)) { | 15 | system.HIDCore(), system.IsPoweredOn(), false)) { |
| 16 | ui->setupUi(this); | 16 | ui->setupUi(this); |
| 17 | 17 | ||
| 18 | ui->controllerLayout->addWidget(profile_widget); | 18 | ui->controllerLayout->addWidget(profile_widget); |
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index f8e08c422..8539a5c8b 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp | |||
| @@ -15,9 +15,9 @@ | |||
| 15 | 15 | ||
| 16 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 17 | #include "common/settings.h" | 17 | #include "common/settings.h" |
| 18 | #include "input_common/drivers/udp_client.h" | ||
| 19 | #include "input_common/helpers/udp_protocol.h" | ||
| 18 | #include "input_common/main.h" | 20 | #include "input_common/main.h" |
| 19 | #include "input_common/udp/client.h" | ||
| 20 | #include "input_common/udp/udp.h" | ||
| 21 | #include "ui_configure_motion_touch.h" | 21 | #include "ui_configure_motion_touch.h" |
| 22 | #include "yuzu/configuration/configure_motion_touch.h" | 22 | #include "yuzu/configuration/configure_motion_touch.h" |
| 23 | #include "yuzu/configuration/configure_touch_from_button.h" | 23 | #include "yuzu/configuration/configure_touch_from_button.h" |
| @@ -93,6 +93,7 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent, | |||
| 93 | "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " | 93 | "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " |
| 94 | "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); | 94 | "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); |
| 95 | 95 | ||
| 96 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 96 | SetConfiguration(); | 97 | SetConfiguration(); |
| 97 | UpdateUiDisplay(); | 98 | UpdateUiDisplay(); |
| 98 | ConnectEvents(); | 99 | ConnectEvents(); |
| @@ -101,17 +102,14 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent, | |||
| 101 | ConfigureMotionTouch::~ConfigureMotionTouch() = default; | 102 | ConfigureMotionTouch::~ConfigureMotionTouch() = default; |
| 102 | 103 | ||
| 103 | void ConfigureMotionTouch::SetConfiguration() { | 104 | void ConfigureMotionTouch::SetConfiguration() { |
| 104 | const Common::ParamPackage motion_param(Settings::values.motion_device.GetValue()); | ||
| 105 | const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); | 105 | const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); |
| 106 | 106 | ||
| 107 | ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button.GetValue()); | ||
| 108 | touch_from_button_maps = Settings::values.touch_from_button_maps; | 107 | touch_from_button_maps = Settings::values.touch_from_button_maps; |
| 109 | for (const auto& touch_map : touch_from_button_maps) { | 108 | for (const auto& touch_map : touch_from_button_maps) { |
| 110 | ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); | 109 | ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); |
| 111 | } | 110 | } |
| 112 | ui->touch_from_button_map->setCurrentIndex( | 111 | ui->touch_from_button_map->setCurrentIndex( |
| 113 | Settings::values.touch_from_button_map_index.GetValue()); | 112 | Settings::values.touch_from_button_map_index.GetValue()); |
| 114 | ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f)); | ||
| 115 | 113 | ||
| 116 | min_x = touch_param.Get("min_x", 100); | 114 | min_x = touch_param.Get("min_x", 100); |
| 117 | min_y = touch_param.Get("min_y", 50); | 115 | min_y = touch_param.Get("min_y", 50); |
| @@ -139,9 +137,6 @@ void ConfigureMotionTouch::SetConfiguration() { | |||
| 139 | void ConfigureMotionTouch::UpdateUiDisplay() { | 137 | void ConfigureMotionTouch::UpdateUiDisplay() { |
| 140 | const QString cemuhook_udp = QStringLiteral("cemuhookudp"); | 138 | const QString cemuhook_udp = QStringLiteral("cemuhookudp"); |
| 141 | 139 | ||
| 142 | ui->motion_sensitivity_label->setVisible(true); | ||
| 143 | ui->motion_sensitivity->setVisible(true); | ||
| 144 | |||
| 145 | ui->touch_calibration->setVisible(true); | 140 | ui->touch_calibration->setVisible(true); |
| 146 | ui->touch_calibration_config->setVisible(true); | 141 | ui->touch_calibration_config->setVisible(true); |
| 147 | ui->touch_calibration_label->setVisible(true); | 142 | ui->touch_calibration_label->setVisible(true); |
| @@ -312,7 +307,6 @@ void ConfigureMotionTouch::ApplyConfiguration() { | |||
| 312 | touch_param.Set("max_y", max_y); | 307 | touch_param.Set("max_y", max_y); |
| 313 | 308 | ||
| 314 | Settings::values.touch_device = touch_param.Serialize(); | 309 | Settings::values.touch_device = touch_param.Serialize(); |
| 315 | Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked(); | ||
| 316 | Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex(); | 310 | Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex(); |
| 317 | Settings::values.touch_from_button_maps = touch_from_button_maps; | 311 | Settings::values.touch_from_button_maps = touch_from_button_maps; |
| 318 | Settings::values.udp_input_servers = GetUDPServerString(); | 312 | Settings::values.udp_input_servers = GetUDPServerString(); |
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui index 1e35ea946..c75a84ae4 100644 --- a/src/yuzu/configuration/configure_motion_touch.ui +++ b/src/yuzu/configuration/configure_motion_touch.ui | |||
| @@ -2,14 +2,6 @@ | |||
| 2 | <ui version="4.0"> | 2 | <ui version="4.0"> |
| 3 | <class>ConfigureMotionTouch</class> | 3 | <class>ConfigureMotionTouch</class> |
| 4 | <widget class="QDialog" name="ConfigureMotionTouch"> | 4 | <widget class="QDialog" name="ConfigureMotionTouch"> |
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>500</width> | ||
| 10 | <height>482</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | 5 | <property name="windowTitle"> |
| 14 | <string>Configure Motion / Touch</string> | 6 | <string>Configure Motion / Touch</string> |
| 15 | </property> | 7 | </property> |
| @@ -18,48 +10,6 @@ | |||
| 18 | </property> | 10 | </property> |
| 19 | <layout class="QVBoxLayout"> | 11 | <layout class="QVBoxLayout"> |
| 20 | <item> | 12 | <item> |
| 21 | <widget class="QGroupBox" name="motion_group_box"> | ||
| 22 | <property name="title"> | ||
| 23 | <string>Mouse Motion</string> | ||
| 24 | </property> | ||
| 25 | <layout class="QVBoxLayout"> | ||
| 26 | <item> | ||
| 27 | <layout class="QHBoxLayout"> | ||
| 28 | <item> | ||
| 29 | <widget class="QLabel" name="motion_sensitivity_label"> | ||
| 30 | <property name="text"> | ||
| 31 | <string>Sensitivity:</string> | ||
| 32 | </property> | ||
| 33 | </widget> | ||
| 34 | </item> | ||
| 35 | <item> | ||
| 36 | <widget class="QDoubleSpinBox" name="motion_sensitivity"> | ||
| 37 | <property name="alignment"> | ||
| 38 | <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||
| 39 | </property> | ||
| 40 | <property name="decimals"> | ||
| 41 | <number>4</number> | ||
| 42 | </property> | ||
| 43 | <property name="minimum"> | ||
| 44 | <double>0.010000000000000</double> | ||
| 45 | </property> | ||
| 46 | <property name="maximum"> | ||
| 47 | <double>10.000000000000000</double> | ||
| 48 | </property> | ||
| 49 | <property name="singleStep"> | ||
| 50 | <double>0.001000000000000</double> | ||
| 51 | </property> | ||
| 52 | <property name="value"> | ||
| 53 | <double>0.010000000000000</double> | ||
| 54 | </property> | ||
| 55 | </widget> | ||
| 56 | </item> | ||
| 57 | </layout> | ||
| 58 | </item> | ||
| 59 | </layout> | ||
| 60 | </widget> | ||
| 61 | </item> | ||
| 62 | <item> | ||
| 63 | <widget class="QGroupBox" name="touch_group_box"> | 13 | <widget class="QGroupBox" name="touch_group_box"> |
| 64 | <property name="title"> | 14 | <property name="title"> |
| 65 | <string>Touch</string> | 15 | <string>Touch</string> |
| @@ -101,19 +51,13 @@ | |||
| 101 | </item> | 51 | </item> |
| 102 | <item> | 52 | <item> |
| 103 | <layout class="QHBoxLayout"> | 53 | <layout class="QHBoxLayout"> |
| 104 | <item> | 54 | <item> |
| 105 | <widget class="QCheckBox" name="touch_from_button_checkbox"> | 55 | <widget class="QLabel" name="touch_from_button_label"> |
| 106 | <property name="sizePolicy"> | 56 | <property name="text"> |
| 107 | <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> | 57 | <string>Touch from button profile:</string> |
| 108 | <horstretch>0</horstretch> | 58 | </property> |
| 109 | <verstretch>0</verstretch> | 59 | </widget> |
| 110 | </sizepolicy> | 60 | </item> |
| 111 | </property> | ||
| 112 | <property name="text"> | ||
| 113 | <string>Use button mapping:</string> | ||
| 114 | </property> | ||
| 115 | </widget> | ||
| 116 | </item> | ||
| 117 | <item> | 61 | <item> |
| 118 | <widget class="QComboBox" name="touch_from_button_map"/> | 62 | <widget class="QComboBox" name="touch_from_button_map"/> |
| 119 | </item> | 63 | </item> |
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp deleted file mode 100644 index 2af3afda8..000000000 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ /dev/null | |||
| @@ -1,276 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include <QKeyEvent> | ||
| 9 | #include <QMenu> | ||
| 10 | #include <QTimer> | ||
| 11 | |||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/param_package.h" | ||
| 14 | #include "input_common/main.h" | ||
| 15 | #include "ui_configure_mouse_advanced.h" | ||
| 16 | #include "yuzu/configuration/config.h" | ||
| 17 | #include "yuzu/configuration/configure_mouse_advanced.h" | ||
| 18 | |||
| 19 | static QString GetKeyName(int key_code) { | ||
| 20 | switch (key_code) { | ||
| 21 | case Qt::LeftButton: | ||
| 22 | return QObject::tr("Click 0"); | ||
| 23 | case Qt::RightButton: | ||
| 24 | return QObject::tr("Click 1"); | ||
| 25 | case Qt::MiddleButton: | ||
| 26 | return QObject::tr("Click 2"); | ||
| 27 | case Qt::BackButton: | ||
| 28 | return QObject::tr("Click 3"); | ||
| 29 | case Qt::ForwardButton: | ||
| 30 | return QObject::tr("Click 4"); | ||
| 31 | case Qt::Key_Shift: | ||
| 32 | return QObject::tr("Shift"); | ||
| 33 | case Qt::Key_Control: | ||
| 34 | return QObject::tr("Ctrl"); | ||
| 35 | case Qt::Key_Alt: | ||
| 36 | return QObject::tr("Alt"); | ||
| 37 | case Qt::Key_Meta: | ||
| 38 | return {}; | ||
| 39 | default: | ||
| 40 | return QKeySequence(key_code).toString(); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | static QString ButtonToText(const Common::ParamPackage& param) { | ||
| 45 | if (!param.Has("engine")) { | ||
| 46 | return QObject::tr("[not set]"); | ||
| 47 | } | ||
| 48 | |||
| 49 | if (param.Get("engine", "") == "keyboard") { | ||
| 50 | return GetKeyName(param.Get("code", 0)); | ||
| 51 | } | ||
| 52 | |||
| 53 | if (param.Get("engine", "") == "sdl") { | ||
| 54 | if (param.Has("hat")) { | ||
| 55 | const QString hat_str = QString::fromStdString(param.Get("hat", "")); | ||
| 56 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); | ||
| 57 | |||
| 58 | return QObject::tr("Hat %1 %2").arg(hat_str, direction_str); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (param.Has("axis")) { | ||
| 62 | const QString axis_str = QString::fromStdString(param.Get("axis", "")); | ||
| 63 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); | ||
| 64 | |||
| 65 | return QObject::tr("Axis %1%2").arg(axis_str, direction_str); | ||
| 66 | } | ||
| 67 | |||
| 68 | if (param.Has("button")) { | ||
| 69 | const QString button_str = QString::fromStdString(param.Get("button", "")); | ||
| 70 | |||
| 71 | return QObject::tr("Button %1").arg(button_str); | ||
| 72 | } | ||
| 73 | return {}; | ||
| 74 | } | ||
| 75 | |||
| 76 | return QObject::tr("[unknown]"); | ||
| 77 | } | ||
| 78 | |||
| 79 | ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent, | ||
| 80 | InputCommon::InputSubsystem* input_subsystem_) | ||
| 81 | : QDialog(parent), | ||
| 82 | ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_}, | ||
| 83 | timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { | ||
| 84 | ui->setupUi(this); | ||
| 85 | setFocusPolicy(Qt::ClickFocus); | ||
| 86 | |||
| 87 | button_map = { | ||
| 88 | ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button, | ||
| 89 | }; | ||
| 90 | |||
| 91 | for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { | ||
| 92 | auto* const button = button_map[button_id]; | ||
| 93 | if (button == nullptr) { | ||
| 94 | continue; | ||
| 95 | } | ||
| 96 | |||
| 97 | button->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 98 | connect(button, &QPushButton::clicked, [=, this] { | ||
| 99 | HandleClick( | ||
| 100 | button_map[button_id], | ||
| 101 | [=, this](const Common::ParamPackage& params) { | ||
| 102 | buttons_param[button_id] = params; | ||
| 103 | }, | ||
| 104 | InputCommon::Polling::DeviceType::Button); | ||
| 105 | }); | ||
| 106 | connect(button, &QPushButton::customContextMenuRequested, | ||
| 107 | [=, this](const QPoint& menu_location) { | ||
| 108 | QMenu context_menu; | ||
| 109 | context_menu.addAction(tr("Clear"), [&] { | ||
| 110 | buttons_param[button_id].Clear(); | ||
| 111 | button_map[button_id]->setText(tr("[not set]")); | ||
| 112 | }); | ||
| 113 | context_menu.addAction(tr("Restore Default"), [&] { | ||
| 114 | buttons_param[button_id] = | ||
| 115 | Common::ParamPackage{InputCommon::GenerateKeyboardParam( | ||
| 116 | Config::default_mouse_buttons[button_id])}; | ||
| 117 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | ||
| 118 | }); | ||
| 119 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | ||
| 120 | }); | ||
| 121 | } | ||
| 122 | |||
| 123 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); | ||
| 124 | connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); | ||
| 125 | |||
| 126 | timeout_timer->setSingleShot(true); | ||
| 127 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); | ||
| 128 | |||
| 129 | connect(poll_timer.get(), &QTimer::timeout, [this] { | ||
| 130 | Common::ParamPackage params; | ||
| 131 | for (auto& poller : device_pollers) { | ||
| 132 | params = poller->GetNextInput(); | ||
| 133 | if (params.Has("engine")) { | ||
| 134 | SetPollingResult(params, false); | ||
| 135 | return; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | }); | ||
| 139 | |||
| 140 | LoadConfiguration(); | ||
| 141 | resize(0, 0); | ||
| 142 | } | ||
| 143 | |||
| 144 | ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default; | ||
| 145 | |||
| 146 | void ConfigureMouseAdvanced::ApplyConfiguration() { | ||
| 147 | std::transform(buttons_param.begin(), buttons_param.end(), | ||
| 148 | Settings::values.mouse_buttons.begin(), | ||
| 149 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 150 | } | ||
| 151 | |||
| 152 | void ConfigureMouseAdvanced::LoadConfiguration() { | ||
| 153 | std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(), | ||
| 154 | buttons_param.begin(), | ||
| 155 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 156 | UpdateButtonLabels(); | ||
| 157 | } | ||
| 158 | |||
| 159 | void ConfigureMouseAdvanced::changeEvent(QEvent* event) { | ||
| 160 | if (event->type() == QEvent::LanguageChange) { | ||
| 161 | RetranslateUI(); | ||
| 162 | } | ||
| 163 | |||
| 164 | QDialog::changeEvent(event); | ||
| 165 | } | ||
| 166 | |||
| 167 | void ConfigureMouseAdvanced::RetranslateUI() { | ||
| 168 | ui->retranslateUi(this); | ||
| 169 | } | ||
| 170 | |||
| 171 | void ConfigureMouseAdvanced::RestoreDefaults() { | ||
| 172 | for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { | ||
| 173 | buttons_param[button_id] = Common::ParamPackage{ | ||
| 174 | InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])}; | ||
| 175 | } | ||
| 176 | |||
| 177 | UpdateButtonLabels(); | ||
| 178 | } | ||
| 179 | |||
| 180 | void ConfigureMouseAdvanced::ClearAll() { | ||
| 181 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 182 | const auto* const button = button_map[i]; | ||
| 183 | if (button != nullptr && button->isEnabled()) { | ||
| 184 | buttons_param[i].Clear(); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | UpdateButtonLabels(); | ||
| 189 | } | ||
| 190 | |||
| 191 | void ConfigureMouseAdvanced::UpdateButtonLabels() { | ||
| 192 | for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) { | ||
| 193 | button_map[button]->setText(ButtonToText(buttons_param[button])); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | void ConfigureMouseAdvanced::HandleClick( | ||
| 198 | QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, | ||
| 199 | InputCommon::Polling::DeviceType type) { | ||
| 200 | button->setText(tr("[press key]")); | ||
| 201 | button->setFocus(); | ||
| 202 | |||
| 203 | // Keyboard keys or mouse buttons can only be used as button devices | ||
| 204 | want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button; | ||
| 205 | if (want_keyboard_mouse) { | ||
| 206 | const auto iter = std::find(button_map.begin(), button_map.end(), button); | ||
| 207 | ASSERT(iter != button_map.end()); | ||
| 208 | const auto index = std::distance(button_map.begin(), iter); | ||
| 209 | ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); | ||
| 210 | } | ||
| 211 | |||
| 212 | input_setter = new_input_setter; | ||
| 213 | |||
| 214 | device_pollers = input_subsystem->GetPollers(type); | ||
| 215 | |||
| 216 | for (auto& poller : device_pollers) { | ||
| 217 | poller->Start(); | ||
| 218 | } | ||
| 219 | |||
| 220 | QWidget::grabMouse(); | ||
| 221 | QWidget::grabKeyboard(); | ||
| 222 | |||
| 223 | timeout_timer->start(2500); // Cancel after 2.5 seconds | ||
| 224 | poll_timer->start(50); // Check for new inputs every 50ms | ||
| 225 | } | ||
| 226 | |||
| 227 | void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) { | ||
| 228 | timeout_timer->stop(); | ||
| 229 | poll_timer->stop(); | ||
| 230 | for (auto& poller : device_pollers) { | ||
| 231 | poller->Stop(); | ||
| 232 | } | ||
| 233 | |||
| 234 | QWidget::releaseMouse(); | ||
| 235 | QWidget::releaseKeyboard(); | ||
| 236 | |||
| 237 | if (!abort) { | ||
| 238 | (*input_setter)(params); | ||
| 239 | } | ||
| 240 | |||
| 241 | UpdateButtonLabels(); | ||
| 242 | input_setter = std::nullopt; | ||
| 243 | } | ||
| 244 | |||
| 245 | void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) { | ||
| 246 | if (!input_setter || !event) { | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | |||
| 250 | if (want_keyboard_mouse) { | ||
| 251 | SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())}, | ||
| 252 | false); | ||
| 253 | } else { | ||
| 254 | // We don't want any mouse buttons, so don't stop polling | ||
| 255 | return; | ||
| 256 | } | ||
| 257 | |||
| 258 | SetPollingResult({}, true); | ||
| 259 | } | ||
| 260 | |||
| 261 | void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) { | ||
| 262 | if (!input_setter || !event) { | ||
| 263 | return; | ||
| 264 | } | ||
| 265 | |||
| 266 | if (event->key() != Qt::Key_Escape) { | ||
| 267 | if (want_keyboard_mouse) { | ||
| 268 | SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, | ||
| 269 | false); | ||
| 270 | } else { | ||
| 271 | // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling | ||
| 272 | return; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | SetPollingResult({}, true); | ||
| 276 | } | ||
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h deleted file mode 100644 index 65b6fca9a..000000000 --- a/src/yuzu/configuration/configure_mouse_advanced.h +++ /dev/null | |||
| @@ -1,78 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra 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 | #include <optional> | ||
| 9 | #include <QDialog> | ||
| 10 | |||
| 11 | class QCheckBox; | ||
| 12 | class QPushButton; | ||
| 13 | class QTimer; | ||
| 14 | |||
| 15 | namespace InputCommon { | ||
| 16 | class InputSubsystem; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Ui { | ||
| 20 | class ConfigureMouseAdvanced; | ||
| 21 | } | ||
| 22 | |||
| 23 | class ConfigureMouseAdvanced : public QDialog { | ||
| 24 | Q_OBJECT | ||
| 25 | |||
| 26 | public: | ||
| 27 | explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_); | ||
| 28 | ~ConfigureMouseAdvanced() override; | ||
| 29 | |||
| 30 | void ApplyConfiguration(); | ||
| 31 | |||
| 32 | private: | ||
| 33 | void changeEvent(QEvent* event) override; | ||
| 34 | void RetranslateUI(); | ||
| 35 | |||
| 36 | /// Load configuration settings. | ||
| 37 | void LoadConfiguration(); | ||
| 38 | /// Restore all buttons to their default values. | ||
| 39 | void RestoreDefaults(); | ||
| 40 | /// Clear all input configuration | ||
| 41 | void ClearAll(); | ||
| 42 | |||
| 43 | /// Update UI to reflect current configuration. | ||
| 44 | void UpdateButtonLabels(); | ||
| 45 | |||
| 46 | /// Called when the button was pressed. | ||
| 47 | void HandleClick(QPushButton* button, | ||
| 48 | std::function<void(const Common::ParamPackage&)> new_input_setter, | ||
| 49 | InputCommon::Polling::DeviceType type); | ||
| 50 | |||
| 51 | /// Finish polling and configure input using the input_setter | ||
| 52 | void SetPollingResult(const Common::ParamPackage& params, bool abort); | ||
| 53 | |||
| 54 | /// Handle mouse button press events. | ||
| 55 | void mousePressEvent(QMouseEvent* event) override; | ||
| 56 | |||
| 57 | /// Handle key press events. | ||
| 58 | void keyPressEvent(QKeyEvent* event) override; | ||
| 59 | |||
| 60 | std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; | ||
| 61 | |||
| 62 | InputCommon::InputSubsystem* input_subsystem; | ||
| 63 | |||
| 64 | /// This will be the the setting function when an input is awaiting configuration. | ||
| 65 | std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; | ||
| 66 | |||
| 67 | std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map; | ||
| 68 | std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param; | ||
| 69 | |||
| 70 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; | ||
| 71 | |||
| 72 | std::unique_ptr<QTimer> timeout_timer; | ||
| 73 | std::unique_ptr<QTimer> poll_timer; | ||
| 74 | |||
| 75 | /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, | ||
| 76 | /// keyboard events are ignored. | ||
| 77 | bool want_keyboard_mouse = false; | ||
| 78 | }; | ||
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui deleted file mode 100644 index 5b99e1c37..000000000 --- a/src/yuzu/configuration/configure_mouse_advanced.ui +++ /dev/null | |||
| @@ -1,335 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureMouseAdvanced</class> | ||
| 4 | <widget class="QDialog" name="ConfigureMouseAdvanced"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>310</width> | ||
| 10 | <height>193</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Configure Mouse</string> | ||
| 15 | </property> | ||
| 16 | <property name="styleSheet"> | ||
| 17 | <string notr="true">QPushButton { | ||
| 18 | min-width: 60px; | ||
| 19 | }</string> | ||
| 20 | </property> | ||
| 21 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 22 | <item> | ||
| 23 | <widget class="QGroupBox" name="gridGroupBox"> | ||
| 24 | <property name="title"> | ||
| 25 | <string>Mouse Buttons</string> | ||
| 26 | </property> | ||
| 27 | <layout class="QGridLayout" name="gridLayout"> | ||
| 28 | <item row="3" column="5"> | ||
| 29 | <layout class="QVBoxLayout" name="verticalLayout_6"> | ||
| 30 | <item> | ||
| 31 | <layout class="QHBoxLayout" name="horizontalLayout_5"> | ||
| 32 | <item> | ||
| 33 | <widget class="QLabel" name="label_5"> | ||
| 34 | <property name="text"> | ||
| 35 | <string>Forward:</string> | ||
| 36 | </property> | ||
| 37 | </widget> | ||
| 38 | </item> | ||
| 39 | </layout> | ||
| 40 | </item> | ||
| 41 | <item> | ||
| 42 | <widget class="QPushButton" name="forward_button"> | ||
| 43 | <property name="minimumSize"> | ||
| 44 | <size> | ||
| 45 | <width>68</width> | ||
| 46 | <height>0</height> | ||
| 47 | </size> | ||
| 48 | </property> | ||
| 49 | <property name="maximumSize"> | ||
| 50 | <size> | ||
| 51 | <width>68</width> | ||
| 52 | <height>16777215</height> | ||
| 53 | </size> | ||
| 54 | </property> | ||
| 55 | <property name="text"> | ||
| 56 | <string/> | ||
| 57 | </property> | ||
| 58 | </widget> | ||
| 59 | </item> | ||
| 60 | </layout> | ||
| 61 | </item> | ||
| 62 | <item row="3" column="1"> | ||
| 63 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 64 | <item> | ||
| 65 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 66 | <item> | ||
| 67 | <widget class="QLabel" name="label_4"> | ||
| 68 | <property name="minimumSize"> | ||
| 69 | <size> | ||
| 70 | <width>54</width> | ||
| 71 | <height>0</height> | ||
| 72 | </size> | ||
| 73 | </property> | ||
| 74 | <property name="text"> | ||
| 75 | <string>Back:</string> | ||
| 76 | </property> | ||
| 77 | </widget> | ||
| 78 | </item> | ||
| 79 | </layout> | ||
| 80 | </item> | ||
| 81 | <item> | ||
| 82 | <widget class="QPushButton" name="back_button"> | ||
| 83 | <property name="minimumSize"> | ||
| 84 | <size> | ||
| 85 | <width>68</width> | ||
| 86 | <height>0</height> | ||
| 87 | </size> | ||
| 88 | </property> | ||
| 89 | <property name="text"> | ||
| 90 | <string/> | ||
| 91 | </property> | ||
| 92 | </widget> | ||
| 93 | </item> | ||
| 94 | </layout> | ||
| 95 | </item> | ||
| 96 | <item row="0" column="1"> | ||
| 97 | <layout class="QVBoxLayout" name="verticalLayout_2"> | ||
| 98 | <item> | ||
| 99 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 100 | <item> | ||
| 101 | <widget class="QLabel" name="label"> | ||
| 102 | <property name="text"> | ||
| 103 | <string>Left:</string> | ||
| 104 | </property> | ||
| 105 | </widget> | ||
| 106 | </item> | ||
| 107 | </layout> | ||
| 108 | </item> | ||
| 109 | <item> | ||
| 110 | <widget class="QPushButton" name="left_button"> | ||
| 111 | <property name="minimumSize"> | ||
| 112 | <size> | ||
| 113 | <width>68</width> | ||
| 114 | <height>0</height> | ||
| 115 | </size> | ||
| 116 | </property> | ||
| 117 | <property name="text"> | ||
| 118 | <string/> | ||
| 119 | </property> | ||
| 120 | </widget> | ||
| 121 | </item> | ||
| 122 | </layout> | ||
| 123 | </item> | ||
| 124 | <item row="0" column="3"> | ||
| 125 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 126 | <item> | ||
| 127 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 128 | <item> | ||
| 129 | <widget class="QLabel" name="label_2"> | ||
| 130 | <property name="text"> | ||
| 131 | <string>Middle:</string> | ||
| 132 | </property> | ||
| 133 | </widget> | ||
| 134 | </item> | ||
| 135 | </layout> | ||
| 136 | </item> | ||
| 137 | <item> | ||
| 138 | <widget class="QPushButton" name="middle_button"> | ||
| 139 | <property name="minimumSize"> | ||
| 140 | <size> | ||
| 141 | <width>68</width> | ||
| 142 | <height>0</height> | ||
| 143 | </size> | ||
| 144 | </property> | ||
| 145 | <property name="maximumSize"> | ||
| 146 | <size> | ||
| 147 | <width>68</width> | ||
| 148 | <height>16777215</height> | ||
| 149 | </size> | ||
| 150 | </property> | ||
| 151 | <property name="text"> | ||
| 152 | <string/> | ||
| 153 | </property> | ||
| 154 | </widget> | ||
| 155 | </item> | ||
| 156 | </layout> | ||
| 157 | </item> | ||
| 158 | <item row="0" column="6"> | ||
| 159 | <spacer name="horizontalSpacer_2"> | ||
| 160 | <property name="orientation"> | ||
| 161 | <enum>Qt::Horizontal</enum> | ||
| 162 | </property> | ||
| 163 | <property name="sizeType"> | ||
| 164 | <enum>QSizePolicy::Fixed</enum> | ||
| 165 | </property> | ||
| 166 | <property name="sizeHint" stdset="0"> | ||
| 167 | <size> | ||
| 168 | <width>0</width> | ||
| 169 | <height>20</height> | ||
| 170 | </size> | ||
| 171 | </property> | ||
| 172 | </spacer> | ||
| 173 | </item> | ||
| 174 | <item row="0" column="0"> | ||
| 175 | <spacer name="horizontalSpacer"> | ||
| 176 | <property name="orientation"> | ||
| 177 | <enum>Qt::Horizontal</enum> | ||
| 178 | </property> | ||
| 179 | <property name="sizeType"> | ||
| 180 | <enum>QSizePolicy::Fixed</enum> | ||
| 181 | </property> | ||
| 182 | <property name="sizeHint" stdset="0"> | ||
| 183 | <size> | ||
| 184 | <width>0</width> | ||
| 185 | <height>20</height> | ||
| 186 | </size> | ||
| 187 | </property> | ||
| 188 | </spacer> | ||
| 189 | </item> | ||
| 190 | <item row="0" column="5"> | ||
| 191 | <layout class="QVBoxLayout" name="verticalLayout_4"> | ||
| 192 | <item> | ||
| 193 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 194 | <item> | ||
| 195 | <widget class="QLabel" name="label_3"> | ||
| 196 | <property name="text"> | ||
| 197 | <string>Right:</string> | ||
| 198 | </property> | ||
| 199 | </widget> | ||
| 200 | </item> | ||
| 201 | </layout> | ||
| 202 | </item> | ||
| 203 | <item> | ||
| 204 | <widget class="QPushButton" name="right_button"> | ||
| 205 | <property name="minimumSize"> | ||
| 206 | <size> | ||
| 207 | <width>68</width> | ||
| 208 | <height>0</height> | ||
| 209 | </size> | ||
| 210 | </property> | ||
| 211 | <property name="maximumSize"> | ||
| 212 | <size> | ||
| 213 | <width>68</width> | ||
| 214 | <height>16777215</height> | ||
| 215 | </size> | ||
| 216 | </property> | ||
| 217 | <property name="text"> | ||
| 218 | <string/> | ||
| 219 | </property> | ||
| 220 | </widget> | ||
| 221 | </item> | ||
| 222 | </layout> | ||
| 223 | </item> | ||
| 224 | <item row="0" column="2"> | ||
| 225 | <spacer name="horizontalSpacer_4"> | ||
| 226 | <property name="orientation"> | ||
| 227 | <enum>Qt::Horizontal</enum> | ||
| 228 | </property> | ||
| 229 | <property name="sizeHint" stdset="0"> | ||
| 230 | <size> | ||
| 231 | <width>0</width> | ||
| 232 | <height>20</height> | ||
| 233 | </size> | ||
| 234 | </property> | ||
| 235 | </spacer> | ||
| 236 | </item> | ||
| 237 | <item row="0" column="4"> | ||
| 238 | <spacer name="horizontalSpacer_5"> | ||
| 239 | <property name="orientation"> | ||
| 240 | <enum>Qt::Horizontal</enum> | ||
| 241 | </property> | ||
| 242 | <property name="sizeHint" stdset="0"> | ||
| 243 | <size> | ||
| 244 | <width>0</width> | ||
| 245 | <height>20</height> | ||
| 246 | </size> | ||
| 247 | </property> | ||
| 248 | </spacer> | ||
| 249 | </item> | ||
| 250 | </layout> | ||
| 251 | </widget> | ||
| 252 | </item> | ||
| 253 | <item> | ||
| 254 | <layout class="QHBoxLayout" name="horizontalLayout_6"> | ||
| 255 | <item> | ||
| 256 | <widget class="QPushButton" name="buttonClearAll"> | ||
| 257 | <property name="minimumSize"> | ||
| 258 | <size> | ||
| 259 | <width>68</width> | ||
| 260 | <height>0</height> | ||
| 261 | </size> | ||
| 262 | </property> | ||
| 263 | <property name="maximumSize"> | ||
| 264 | <size> | ||
| 265 | <width>68</width> | ||
| 266 | <height>16777215</height> | ||
| 267 | </size> | ||
| 268 | </property> | ||
| 269 | <property name="text"> | ||
| 270 | <string>Clear</string> | ||
| 271 | </property> | ||
| 272 | </widget> | ||
| 273 | </item> | ||
| 274 | <item> | ||
| 275 | <widget class="QPushButton" name="buttonRestoreDefaults"> | ||
| 276 | <property name="minimumSize"> | ||
| 277 | <size> | ||
| 278 | <width>68</width> | ||
| 279 | <height>0</height> | ||
| 280 | </size> | ||
| 281 | </property> | ||
| 282 | <property name="maximumSize"> | ||
| 283 | <size> | ||
| 284 | <width>68</width> | ||
| 285 | <height>16777215</height> | ||
| 286 | </size> | ||
| 287 | </property> | ||
| 288 | <property name="text"> | ||
| 289 | <string>Defaults</string> | ||
| 290 | </property> | ||
| 291 | </widget> | ||
| 292 | </item> | ||
| 293 | <item> | ||
| 294 | <spacer name="horizontalSpacer_3"> | ||
| 295 | <property name="orientation"> | ||
| 296 | <enum>Qt::Horizontal</enum> | ||
| 297 | </property> | ||
| 298 | <property name="sizeHint" stdset="0"> | ||
| 299 | <size> | ||
| 300 | <width>0</width> | ||
| 301 | <height>20</height> | ||
| 302 | </size> | ||
| 303 | </property> | ||
| 304 | </spacer> | ||
| 305 | </item> | ||
| 306 | <item> | ||
| 307 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 308 | <property name="styleSheet"> | ||
| 309 | <string notr="true"/> | ||
| 310 | </property> | ||
| 311 | <property name="standardButtons"> | ||
| 312 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 313 | </property> | ||
| 314 | </widget> | ||
| 315 | </item> | ||
| 316 | </layout> | ||
| 317 | </item> | ||
| 318 | </layout> | ||
| 319 | </widget> | ||
| 320 | <resources/> | ||
| 321 | <connections> | ||
| 322 | <connection> | ||
| 323 | <sender>buttonBox</sender> | ||
| 324 | <signal>accepted()</signal> | ||
| 325 | <receiver>ConfigureMouseAdvanced</receiver> | ||
| 326 | <slot>accept()</slot> | ||
| 327 | </connection> | ||
| 328 | <connection> | ||
| 329 | <sender>buttonBox</sender> | ||
| 330 | <signal>rejected()</signal> | ||
| 331 | <receiver>ConfigureMouseAdvanced</receiver> | ||
| 332 | <slot>reject()</slot> | ||
| 333 | </connection> | ||
| 334 | </connections> | ||
| 335 | </ui> | ||
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp index 8e5a4c72d..979a8db61 100644 --- a/src/yuzu/configuration/configure_tas.cpp +++ b/src/yuzu/configuration/configure_tas.cpp | |||
| @@ -32,7 +32,6 @@ void ConfigureTasDialog::LoadConfiguration() { | |||
| 32 | ui->tas_path_edit->setText( | 32 | ui->tas_path_edit->setText( |
| 33 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir))); | 33 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir))); |
| 34 | ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue()); | 34 | ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue()); |
| 35 | ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers.GetValue()); | ||
| 36 | ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue()); | 35 | ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue()); |
| 37 | ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue()); | 36 | ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue()); |
| 38 | } | 37 | } |
| @@ -40,7 +39,6 @@ void ConfigureTasDialog::LoadConfiguration() { | |||
| 40 | void ConfigureTasDialog::ApplyConfiguration() { | 39 | void ConfigureTasDialog::ApplyConfiguration() { |
| 41 | Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString()); | 40 | Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString()); |
| 42 | Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked()); | 41 | Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked()); |
| 43 | Settings::values.tas_swap_controllers.SetValue(ui->tas_control_swap->isChecked()); | ||
| 44 | Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked()); | 42 | Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked()); |
| 45 | Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked()); | 43 | Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked()); |
| 46 | } | 44 | } |
diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui index 7d44895c4..cf88a5bf0 100644 --- a/src/yuzu/configuration/configure_tas.ui +++ b/src/yuzu/configuration/configure_tas.ui | |||
| @@ -59,20 +59,13 @@ | |||
| 59 | </widget> | 59 | </widget> |
| 60 | </item> | 60 | </item> |
| 61 | <item row="1" column="0" colspan="4"> | 61 | <item row="1" column="0" colspan="4"> |
| 62 | <widget class="QCheckBox" name="tas_control_swap"> | ||
| 63 | <property name="text"> | ||
| 64 | <string>Automatic controller profile swapping</string> | ||
| 65 | </property> | ||
| 66 | </widget> | ||
| 67 | </item> | ||
| 68 | <item row="2" column="0" colspan="4"> | ||
| 69 | <widget class="QCheckBox" name="tas_loop_script"> | 62 | <widget class="QCheckBox" name="tas_loop_script"> |
| 70 | <property name="text"> | 63 | <property name="text"> |
| 71 | <string>Loop script</string> | 64 | <string>Loop script</string> |
| 72 | </property> | 65 | </property> |
| 73 | </widget> | 66 | </widget> |
| 74 | </item> | 67 | </item> |
| 75 | <item row="3" column="0" colspan="4"> | 68 | <item row="2" column="0" colspan="4"> |
| 76 | <widget class="QCheckBox" name="tas_pause_on_load"> | 69 | <widget class="QCheckBox" name="tas_pause_on_load"> |
| 77 | <property name="enabled"> | 70 | <property name="enabled"> |
| 78 | <bool>false</bool> | 71 | <bool>false</bool> |
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp index 40129f228..bde0a08c4 100644 --- a/src/yuzu/configuration/configure_touch_from_button.cpp +++ b/src/yuzu/configuration/configure_touch_from_button.cpp | |||
| @@ -163,13 +163,10 @@ void ConfigureTouchFromButton::ConnectEvents() { | |||
| 163 | connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); }); | 163 | connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); }); |
| 164 | 164 | ||
| 165 | connect(poll_timer.get(), &QTimer::timeout, [this]() { | 165 | connect(poll_timer.get(), &QTimer::timeout, [this]() { |
| 166 | Common::ParamPackage params; | 166 | const auto& params = input_subsystem->GetNextInput(); |
| 167 | for (auto& poller : device_pollers) { | 167 | if (params.Has("engine")) { |
| 168 | params = poller->GetNextInput(); | 168 | SetPollingResult(params, false); |
| 169 | if (params.Has("engine")) { | 169 | return; |
| 170 | SetPollingResult(params, false); | ||
| 171 | return; | ||
| 172 | } | ||
| 173 | } | 170 | } |
| 174 | }); | 171 | }); |
| 175 | } | 172 | } |
| @@ -248,11 +245,7 @@ void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is | |||
| 248 | } | 245 | } |
| 249 | }; | 246 | }; |
| 250 | 247 | ||
| 251 | device_pollers = input_subsystem->GetPollers(InputCommon::Polling::DeviceType::Button); | 248 | input_subsystem->BeginMapping(InputCommon::Polling::InputType::Button); |
| 252 | |||
| 253 | for (auto& poller : device_pollers) { | ||
| 254 | poller->Start(); | ||
| 255 | } | ||
| 256 | 249 | ||
| 257 | grabKeyboard(); | 250 | grabKeyboard(); |
| 258 | grabMouse(); | 251 | grabMouse(); |
| @@ -365,14 +358,14 @@ void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& po | |||
| 365 | 358 | ||
| 366 | void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params, | 359 | void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params, |
| 367 | const bool cancel) { | 360 | const bool cancel) { |
| 361 | timeout_timer->stop(); | ||
| 362 | poll_timer->stop(); | ||
| 363 | input_subsystem->StopMapping(); | ||
| 364 | |||
| 368 | releaseKeyboard(); | 365 | releaseKeyboard(); |
| 369 | releaseMouse(); | 366 | releaseMouse(); |
| 370 | qApp->restoreOverrideCursor(); | 367 | qApp->restoreOverrideCursor(); |
| 371 | timeout_timer->stop(); | 368 | |
| 372 | poll_timer->stop(); | ||
| 373 | for (auto& poller : device_pollers) { | ||
| 374 | poller->Stop(); | ||
| 375 | } | ||
| 376 | if (input_setter) { | 369 | if (input_setter) { |
| 377 | (*input_setter)(params, cancel); | 370 | (*input_setter)(params, cancel); |
| 378 | input_setter.reset(); | 371 | input_setter.reset(); |
diff --git a/src/yuzu/configuration/configure_touch_from_button.h b/src/yuzu/configuration/configure_touch_from_button.h index d9513e3bc..e1400481a 100644 --- a/src/yuzu/configuration/configure_touch_from_button.h +++ b/src/yuzu/configuration/configure_touch_from_button.h | |||
| @@ -24,10 +24,6 @@ namespace InputCommon { | |||
| 24 | class InputSubsystem; | 24 | class InputSubsystem; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | namespace InputCommon::Polling { | ||
| 28 | class DevicePoller; | ||
| 29 | } | ||
| 30 | |||
| 31 | namespace Settings { | 27 | namespace Settings { |
| 32 | struct TouchFromButtonMap; | 28 | struct TouchFromButtonMap; |
| 33 | } | 29 | } |
| @@ -85,7 +81,6 @@ private: | |||
| 85 | 81 | ||
| 86 | std::unique_ptr<QTimer> timeout_timer; | 82 | std::unique_ptr<QTimer> timeout_timer; |
| 87 | std::unique_ptr<QTimer> poll_timer; | 83 | std::unique_ptr<QTimer> poll_timer; |
| 88 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; | ||
| 89 | std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter; | 84 | std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter; |
| 90 | 85 | ||
| 91 | static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2; | 86 | static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2; |
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp index 46a0f3025..adce04b27 100644 --- a/src/yuzu/configuration/configure_vibration.cpp +++ b/src/yuzu/configuration/configure_vibration.cpp | |||
| @@ -59,80 +59,6 @@ void ConfigureVibration::ApplyConfiguration() { | |||
| 59 | ui->checkBoxAccurateVibration->isChecked()); | 59 | ui->checkBoxAccurateVibration->isChecked()); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | void ConfigureVibration::SetVibrationDevices(std::size_t player_index) { | ||
| 63 | using namespace Settings::NativeButton; | ||
| 64 | static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{ | ||
| 65 | {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons | ||
| 66 | {A, B, X, Y, R, ZR}, // Right Buttons | ||
| 67 | }}; | ||
| 68 | |||
| 69 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 70 | |||
| 71 | for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) { | ||
| 72 | std::unordered_map<std::string, int> params_count; | ||
| 73 | |||
| 74 | for (const auto button_index : buttons[device_idx]) { | ||
| 75 | const auto& player_button = player.buttons[button_index]; | ||
| 76 | |||
| 77 | if (params_count.find(player_button) != params_count.end()) { | ||
| 78 | ++params_count[player_button]; | ||
| 79 | continue; | ||
| 80 | } | ||
| 81 | |||
| 82 | params_count.insert_or_assign(player_button, 1); | ||
| 83 | } | ||
| 84 | |||
| 85 | const auto it = std::max_element( | ||
| 86 | params_count.begin(), params_count.end(), | ||
| 87 | [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }); | ||
| 88 | |||
| 89 | auto& vibration_param_str = player.vibrations[device_idx]; | ||
| 90 | vibration_param_str.clear(); | ||
| 91 | |||
| 92 | if (it->first.empty()) { | ||
| 93 | continue; | ||
| 94 | } | ||
| 95 | |||
| 96 | const auto param = Common::ParamPackage(it->first); | ||
| 97 | |||
| 98 | const auto engine = param.Get("engine", ""); | ||
| 99 | const auto guid = param.Get("guid", ""); | ||
| 100 | const auto port = param.Get("port", ""); | ||
| 101 | |||
| 102 | if (engine.empty() || engine == "keyboard" || engine == "mouse" || engine == "tas") { | ||
| 103 | continue; | ||
| 104 | } | ||
| 105 | |||
| 106 | vibration_param_str += fmt::format("engine:{}", engine); | ||
| 107 | |||
| 108 | if (!port.empty()) { | ||
| 109 | vibration_param_str += fmt::format(",port:{}", port); | ||
| 110 | } | ||
| 111 | if (!guid.empty()) { | ||
| 112 | vibration_param_str += fmt::format(",guid:{}", guid); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | if (player.vibrations[0] != player.vibrations[1]) { | ||
| 117 | return; | ||
| 118 | } | ||
| 119 | |||
| 120 | if (!player.vibrations[0].empty() && | ||
| 121 | player.controller_type != Settings::ControllerType::RightJoycon) { | ||
| 122 | player.vibrations[1].clear(); | ||
| 123 | } else if (!player.vibrations[1].empty() && | ||
| 124 | player.controller_type == Settings::ControllerType::RightJoycon) { | ||
| 125 | player.vibrations[0].clear(); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | void ConfigureVibration::SetAllVibrationDevices() { | ||
| 130 | // Set vibration devices for all player indices including handheld | ||
| 131 | for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) { | ||
| 132 | SetVibrationDevices(player_idx); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | void ConfigureVibration::changeEvent(QEvent* event) { | 62 | void ConfigureVibration::changeEvent(QEvent* event) { |
| 137 | if (event->type() == QEvent::LanguageChange) { | 63 | if (event->type() == QEvent::LanguageChange) { |
| 138 | RetranslateUI(); | 64 | RetranslateUI(); |
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h index 07411a86f..37bbc2653 100644 --- a/src/yuzu/configuration/configure_vibration.h +++ b/src/yuzu/configuration/configure_vibration.h | |||
| @@ -24,9 +24,6 @@ public: | |||
| 24 | 24 | ||
| 25 | void ApplyConfiguration(); | 25 | void ApplyConfiguration(); |
| 26 | 26 | ||
| 27 | static void SetVibrationDevices(std::size_t player_index); | ||
| 28 | static void SetAllVibrationDevices(); | ||
| 29 | |||
| 30 | private: | 27 | private: |
| 31 | void changeEvent(QEvent* event) override; | 28 | void changeEvent(QEvent* event) override; |
| 32 | void RetranslateUI(); | 29 | void RetranslateUI(); |
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp index 5a844409b..6b834c42e 100644 --- a/src/yuzu/debugger/controller.cpp +++ b/src/yuzu/debugger/controller.cpp | |||
| @@ -6,13 +6,17 @@ | |||
| 6 | #include <QLayout> | 6 | #include <QLayout> |
| 7 | #include <QString> | 7 | #include <QString> |
| 8 | #include "common/settings.h" | 8 | #include "common/settings.h" |
| 9 | #include "core/hid/emulated_controller.h" | ||
| 10 | #include "core/hid/hid_core.h" | ||
| 11 | #include "input_common/drivers/tas_input.h" | ||
| 9 | #include "input_common/main.h" | 12 | #include "input_common/main.h" |
| 10 | #include "input_common/tas/tas_input.h" | ||
| 11 | #include "yuzu/configuration/configure_input_player_widget.h" | 13 | #include "yuzu/configuration/configure_input_player_widget.h" |
| 12 | #include "yuzu/debugger/controller.h" | 14 | #include "yuzu/debugger/controller.h" |
| 13 | 15 | ||
| 14 | ControllerDialog::ControllerDialog(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) | 16 | ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_, |
| 15 | : QWidget(parent, Qt::Dialog), input_subsystem{input_subsystem_} { | 17 | std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_, |
| 18 | QWidget* parent) | ||
| 19 | : QWidget(parent, Qt::Dialog), hid_core{hid_core_}, input_subsystem{input_subsystem_} { | ||
| 16 | setObjectName(QStringLiteral("Controller")); | 20 | setObjectName(QStringLiteral("Controller")); |
| 17 | setWindowTitle(tr("Controller P1")); | 21 | setWindowTitle(tr("Controller P1")); |
| 18 | resize(500, 350); | 22 | resize(500, 350); |
| @@ -31,20 +35,24 @@ ControllerDialog::ControllerDialog(QWidget* parent, InputCommon::InputSubsystem* | |||
| 31 | // Configure focus so that widget is focusable and the dialog automatically forwards focus to | 35 | // Configure focus so that widget is focusable and the dialog automatically forwards focus to |
| 32 | // it. | 36 | // it. |
| 33 | setFocusProxy(widget); | 37 | setFocusProxy(widget); |
| 34 | widget->SetConnectedStatus(false); | ||
| 35 | widget->setFocusPolicy(Qt::StrongFocus); | 38 | widget->setFocusPolicy(Qt::StrongFocus); |
| 36 | widget->setFocus(); | 39 | widget->setFocus(); |
| 37 | } | 40 | } |
| 38 | 41 | ||
| 39 | void ControllerDialog::refreshConfiguration() { | 42 | void ControllerDialog::refreshConfiguration() { |
| 40 | const auto& players = Settings::values.players.GetValue(); | 43 | UnloadController(); |
| 41 | constexpr std::size_t player = 0; | 44 | auto* player_1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 42 | widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs); | 45 | auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 43 | widget->SetControllerType(players[player].controller_type); | 46 | // Display the correct controller |
| 44 | ControllerCallback callback{[this](ControllerInput input) { InputController(input); }}; | 47 | controller = handheld->IsConnected() ? handheld : player_1; |
| 45 | widget->SetCallBack(callback); | 48 | |
| 46 | widget->repaint(); | 49 | Core::HID::ControllerUpdateCallback engine_callback{ |
| 47 | widget->SetConnectedStatus(players[player].connected); | 50 | .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); }, |
| 51 | .is_npad_service = true, | ||
| 52 | }; | ||
| 53 | callback_key = controller->SetCallback(engine_callback); | ||
| 54 | widget->SetController(controller); | ||
| 55 | is_controller_set = true; | ||
| 48 | } | 56 | } |
| 49 | 57 | ||
| 50 | QAction* ControllerDialog::toggleViewAction() { | 58 | QAction* ControllerDialog::toggleViewAction() { |
| @@ -58,11 +66,18 @@ QAction* ControllerDialog::toggleViewAction() { | |||
| 58 | return toggle_view_action; | 66 | return toggle_view_action; |
| 59 | } | 67 | } |
| 60 | 68 | ||
| 69 | void ControllerDialog::UnloadController() { | ||
| 70 | widget->UnloadController(); | ||
| 71 | if (is_controller_set) { | ||
| 72 | controller->DeleteCallback(callback_key); | ||
| 73 | is_controller_set = false; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 61 | void ControllerDialog::showEvent(QShowEvent* ev) { | 77 | void ControllerDialog::showEvent(QShowEvent* ev) { |
| 62 | if (toggle_view_action) { | 78 | if (toggle_view_action) { |
| 63 | toggle_view_action->setChecked(isVisible()); | 79 | toggle_view_action->setChecked(isVisible()); |
| 64 | } | 80 | } |
| 65 | refreshConfiguration(); | ||
| 66 | QWidget::showEvent(ev); | 81 | QWidget::showEvent(ev); |
| 67 | } | 82 | } |
| 68 | 83 | ||
| @@ -70,16 +85,34 @@ void ControllerDialog::hideEvent(QHideEvent* ev) { | |||
| 70 | if (toggle_view_action) { | 85 | if (toggle_view_action) { |
| 71 | toggle_view_action->setChecked(isVisible()); | 86 | toggle_view_action->setChecked(isVisible()); |
| 72 | } | 87 | } |
| 73 | widget->SetConnectedStatus(false); | ||
| 74 | QWidget::hideEvent(ev); | 88 | QWidget::hideEvent(ev); |
| 75 | } | 89 | } |
| 76 | 90 | ||
| 77 | void ControllerDialog::InputController(ControllerInput input) { | 91 | void ControllerDialog::ControllerUpdate(Core::HID::ControllerTriggerType type) { |
| 78 | u32 buttons = 0; | 92 | // TODO(german77): Remove TAS from here |
| 79 | int index = 0; | 93 | switch (type) { |
| 80 | for (bool btn : input.button_values) { | 94 | case Core::HID::ControllerTriggerType::Button: |
| 81 | buttons |= (btn ? 1U : 0U) << index; | 95 | case Core::HID::ControllerTriggerType::Stick: { |
| 82 | index++; | 96 | const auto buttons_values = controller->GetButtonsValues(); |
| 97 | const auto stick_values = controller->GetSticksValues(); | ||
| 98 | u64 buttons = 0; | ||
| 99 | std::size_t index = 0; | ||
| 100 | for (const auto& button : buttons_values) { | ||
| 101 | buttons |= button.value ? 1LLU << index : 0; | ||
| 102 | index++; | ||
| 103 | } | ||
| 104 | const InputCommon::TasInput::TasAnalog left_axis = { | ||
| 105 | .x = stick_values[Settings::NativeAnalog::LStick].x.value, | ||
| 106 | .y = stick_values[Settings::NativeAnalog::LStick].y.value, | ||
| 107 | }; | ||
| 108 | const InputCommon::TasInput::TasAnalog right_axis = { | ||
| 109 | .x = stick_values[Settings::NativeAnalog::RStick].x.value, | ||
| 110 | .y = stick_values[Settings::NativeAnalog::RStick].y.value, | ||
| 111 | }; | ||
| 112 | input_subsystem->GetTas()->RecordInput(buttons, left_axis, right_axis); | ||
| 113 | break; | ||
| 114 | } | ||
| 115 | default: | ||
| 116 | break; | ||
| 83 | } | 117 | } |
| 84 | input_subsystem->GetTas()->RecordInput(buttons, input.axis_values); | ||
| 85 | } | 118 | } |
diff --git a/src/yuzu/debugger/controller.h b/src/yuzu/debugger/controller.h index 7742db58b..52cea3326 100644 --- a/src/yuzu/debugger/controller.h +++ b/src/yuzu/debugger/controller.h | |||
| @@ -4,9 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <QFileSystemWatcher> | ||
| 8 | #include <QWidget> | 7 | #include <QWidget> |
| 9 | #include "common/settings.h" | ||
| 10 | 8 | ||
| 11 | class QAction; | 9 | class QAction; |
| 12 | class QHideEvent; | 10 | class QHideEvent; |
| @@ -17,35 +15,43 @@ namespace InputCommon { | |||
| 17 | class InputSubsystem; | 15 | class InputSubsystem; |
| 18 | } | 16 | } |
| 19 | 17 | ||
| 20 | struct ControllerInput { | 18 | namespace Core::HID { |
| 21 | std::array<std::pair<float, float>, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{}; | 19 | class HIDCore; |
| 22 | std::array<bool, Settings::NativeButton::NumButtons> button_values{}; | 20 | class EmulatedController; |
| 23 | bool changed{}; | 21 | enum class ControllerTriggerType; |
| 24 | }; | 22 | } // namespace Core::HID |
| 25 | |||
| 26 | struct ControllerCallback { | ||
| 27 | std::function<void(ControllerInput)> input; | ||
| 28 | }; | ||
| 29 | 23 | ||
| 30 | class ControllerDialog : public QWidget { | 24 | class ControllerDialog : public QWidget { |
| 31 | Q_OBJECT | 25 | Q_OBJECT |
| 32 | 26 | ||
| 33 | public: | 27 | public: |
| 34 | explicit ControllerDialog(QWidget* parent = nullptr, | 28 | explicit ControllerDialog(Core::HID::HIDCore& hid_core_, |
| 35 | InputCommon::InputSubsystem* input_subsystem_ = nullptr); | 29 | std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_, |
| 30 | QWidget* parent = nullptr); | ||
| 36 | 31 | ||
| 37 | /// Returns a QAction that can be used to toggle visibility of this dialog. | 32 | /// Returns a QAction that can be used to toggle visibility of this dialog. |
| 38 | QAction* toggleViewAction(); | 33 | QAction* toggleViewAction(); |
| 34 | |||
| 35 | /// Reloads the widget to apply any changes in the configuration | ||
| 39 | void refreshConfiguration(); | 36 | void refreshConfiguration(); |
| 40 | 37 | ||
| 38 | /// Disables events from the emulated controller | ||
| 39 | void UnloadController(); | ||
| 40 | |||
| 41 | protected: | 41 | protected: |
| 42 | void showEvent(QShowEvent* ev) override; | 42 | void showEvent(QShowEvent* ev) override; |
| 43 | void hideEvent(QHideEvent* ev) override; | 43 | void hideEvent(QHideEvent* ev) override; |
| 44 | 44 | ||
| 45 | private: | 45 | private: |
| 46 | void InputController(ControllerInput input); | 46 | /// Redirects input from the widget to the TAS driver |
| 47 | void ControllerUpdate(Core::HID::ControllerTriggerType type); | ||
| 48 | |||
| 49 | int callback_key; | ||
| 50 | bool is_controller_set{}; | ||
| 51 | Core::HID::EmulatedController* controller; | ||
| 52 | |||
| 47 | QAction* toggle_view_action = nullptr; | 53 | QAction* toggle_view_action = nullptr; |
| 48 | QFileSystemWatcher* watcher = nullptr; | ||
| 49 | PlayerControlPreview* widget; | 54 | PlayerControlPreview* widget; |
| 50 | InputCommon::InputSubsystem* input_subsystem; | 55 | Core::HID::HIDCore& hid_core; |
| 56 | std::shared_ptr<InputCommon::InputSubsystem> input_subsystem; | ||
| 51 | }; | 57 | }; |
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp index a8b254199..33110685a 100644 --- a/src/yuzu/debugger/profiler.cpp +++ b/src/yuzu/debugger/profiler.cpp | |||
| @@ -163,7 +163,7 @@ void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { | |||
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { | 165 | void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { |
| 166 | const auto wheel_position = ev->pos(); | 166 | const auto wheel_position = ev->position().toPoint(); |
| 167 | MicroProfileMousePosition(wheel_position.x() / x_scale, wheel_position.y() / y_scale, | 167 | MicroProfileMousePosition(wheel_position.x() / x_scale, wheel_position.y() / y_scale, |
| 168 | ev->angleDelta().y() / 120); | 168 | ev->angleDelta().y() / 120); |
| 169 | ev->accept(); | 169 | ev->accept(); |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 2af95dbe5..8b5c4a10a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <fmt/format.h> | 17 | #include <fmt/format.h> |
| 18 | #include "common/common_types.h" | 18 | #include "common/common_types.h" |
| 19 | #include "common/logging/log.h" | 19 | #include "common/logging/log.h" |
| 20 | #include "core/core.h" | ||
| 20 | #include "core/file_sys/patch_manager.h" | 21 | #include "core/file_sys/patch_manager.h" |
| 21 | #include "core/file_sys/registered_cache.h" | 22 | #include "core/file_sys/registered_cache.h" |
| 22 | #include "yuzu/compatibility_list.h" | 23 | #include "yuzu/compatibility_list.h" |
| @@ -25,6 +26,7 @@ | |||
| 25 | #include "yuzu/game_list_worker.h" | 26 | #include "yuzu/game_list_worker.h" |
| 26 | #include "yuzu/main.h" | 27 | #include "yuzu/main.h" |
| 27 | #include "yuzu/uisettings.h" | 28 | #include "yuzu/uisettings.h" |
| 29 | #include "yuzu/util/controller_navigation.h" | ||
| 28 | 30 | ||
| 29 | GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent) | 31 | GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent) |
| 30 | : QObject(parent), gamelist{gamelist} {} | 32 | : QObject(parent), gamelist{gamelist} {} |
| @@ -171,13 +173,17 @@ void GameList::OnItemExpanded(const QModelIndex& item) { | |||
| 171 | const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || | 173 | const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || |
| 172 | type == GameListItemType::UserNandDir || | 174 | type == GameListItemType::UserNandDir || |
| 173 | type == GameListItemType::SysNandDir; | 175 | type == GameListItemType::SysNandDir; |
| 174 | 176 | const bool is_fave = type == GameListItemType::Favorites; | |
| 175 | if (!is_dir) { | 177 | if (!is_dir && !is_fave) { |
| 176 | return; | 178 | return; |
| 177 | } | 179 | } |
| 178 | 180 | const bool is_expanded = tree_view->isExpanded(item); | |
| 179 | UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded = | 181 | if (is_fave) { |
| 180 | tree_view->isExpanded(item); | 182 | UISettings::values.favorites_expanded = is_expanded; |
| 183 | return; | ||
| 184 | } | ||
| 185 | const int item_dir_index = item.data(GameListDir::GameDirRole).toInt(); | ||
| 186 | UISettings::values.game_dirs[item_dir_index].expanded = is_expanded; | ||
| 181 | } | 187 | } |
| 182 | 188 | ||
| 183 | // Event in order to filter the gamelist after editing the searchfield | 189 | // Event in order to filter the gamelist after editing the searchfield |
| @@ -312,6 +318,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide | |||
| 312 | this->main_window = parent; | 318 | this->main_window = parent; |
| 313 | layout = new QVBoxLayout; | 319 | layout = new QVBoxLayout; |
| 314 | tree_view = new QTreeView; | 320 | tree_view = new QTreeView; |
| 321 | controller_navigation = new ControllerNavigation(system.HIDCore(), this); | ||
| 315 | search_field = new GameListSearchField(this); | 322 | search_field = new GameListSearchField(this); |
| 316 | item_model = new QStandardItemModel(tree_view); | 323 | item_model = new QStandardItemModel(tree_view); |
| 317 | tree_view->setModel(item_model); | 324 | tree_view->setModel(item_model); |
| @@ -341,6 +348,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide | |||
| 341 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); | 348 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); |
| 342 | connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); | 349 | connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); |
| 343 | connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded); | 350 | connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded); |
| 351 | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, | ||
| 352 | [this](Qt::Key key) { | ||
| 353 | // Avoid pressing buttons while playing | ||
| 354 | if (system.IsPoweredOn()) { | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | if (!this->isActiveWindow()) { | ||
| 358 | return; | ||
| 359 | } | ||
| 360 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | ||
| 361 | QCoreApplication::postEvent(tree_view, event); | ||
| 362 | }); | ||
| 344 | 363 | ||
| 345 | // We must register all custom types with the Qt Automoc system so that we are able to use | 364 | // We must register all custom types with the Qt Automoc system so that we are able to use |
| 346 | // it with signals/slots. In this case, QList falls under the umbrells of custom types. | 365 | // it with signals/slots. In this case, QList falls under the umbrells of custom types. |
| @@ -353,7 +372,12 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide | |||
| 353 | setLayout(layout); | 372 | setLayout(layout); |
| 354 | } | 373 | } |
| 355 | 374 | ||
| 375 | void GameList::UnloadController() { | ||
| 376 | controller_navigation->UnloadController(); | ||
| 377 | } | ||
| 378 | |||
| 356 | GameList::~GameList() { | 379 | GameList::~GameList() { |
| 380 | UnloadController(); | ||
| 357 | emit ShouldCancelWorker(); | 381 | emit ShouldCancelWorker(); |
| 358 | } | 382 | } |
| 359 | 383 | ||
| @@ -438,10 +462,13 @@ void GameList::DonePopulating(const QStringList& watch_list) { | |||
| 438 | emit ShowList(!IsEmpty()); | 462 | emit ShowList(!IsEmpty()); |
| 439 | 463 | ||
| 440 | item_model->invisibleRootItem()->appendRow(new GameListAddDir()); | 464 | item_model->invisibleRootItem()->appendRow(new GameListAddDir()); |
| 465 | |||
| 466 | // Add favorites row | ||
| 441 | item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); | 467 | item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); |
| 442 | tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), | 468 | tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), |
| 443 | UISettings::values.favorited_ids.size() == 0); | 469 | UISettings::values.favorited_ids.size() == 0); |
| 444 | tree_view->expand(item_model->invisibleRootItem()->child(0)->index()); | 470 | tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(), |
| 471 | UISettings::values.favorites_expanded.GetValue()); | ||
| 445 | for (const auto id : UISettings::values.favorited_ids) { | 472 | for (const auto id : UISettings::values.favorited_ids) { |
| 446 | AddFavorite(id); | 473 | AddFavorite(id); |
| 447 | } | 474 | } |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 675469e66..a94ea1477 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "uisettings.h" | 24 | #include "uisettings.h" |
| 25 | #include "yuzu/compatibility_list.h" | 25 | #include "yuzu/compatibility_list.h" |
| 26 | 26 | ||
| 27 | class ControllerNavigation; | ||
| 27 | class GameListWorker; | 28 | class GameListWorker; |
| 28 | class GameListSearchField; | 29 | class GameListSearchField; |
| 29 | class GameListDir; | 30 | class GameListDir; |
| @@ -88,6 +89,9 @@ public: | |||
| 88 | void SaveInterfaceLayout(); | 89 | void SaveInterfaceLayout(); |
| 89 | void LoadInterfaceLayout(); | 90 | void LoadInterfaceLayout(); |
| 90 | 91 | ||
| 92 | /// Disables events from the emulated controller | ||
| 93 | void UnloadController(); | ||
| 94 | |||
| 91 | static const QStringList supported_file_extensions; | 95 | static const QStringList supported_file_extensions; |
| 92 | 96 | ||
| 93 | signals: | 97 | signals: |
| @@ -143,6 +147,7 @@ private: | |||
| 143 | QStandardItemModel* item_model = nullptr; | 147 | QStandardItemModel* item_model = nullptr; |
| 144 | GameListWorker* current_worker = nullptr; | 148 | GameListWorker* current_worker = nullptr; |
| 145 | QFileSystemWatcher* watcher = nullptr; | 149 | QFileSystemWatcher* watcher = nullptr; |
| 150 | ControllerNavigation* controller_navigation = nullptr; | ||
| 146 | CompatibilityList compatibility_list; | 151 | CompatibilityList compatibility_list; |
| 147 | 152 | ||
| 148 | friend class GameListSearchField; | 153 | friend class GameListSearchField; |
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index ae842306c..b001b8c23 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp | |||
| @@ -136,7 +136,7 @@ void LoadingScreen::OnLoadComplete() { | |||
| 136 | void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, | 136 | void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, |
| 137 | std::size_t total) { | 137 | std::size_t total) { |
| 138 | using namespace std::chrono; | 138 | using namespace std::chrono; |
| 139 | const auto now = high_resolution_clock::now(); | 139 | const auto now = steady_clock::now(); |
| 140 | // reset the timer if the stage changes | 140 | // reset the timer if the stage changes |
| 141 | if (stage != previous_stage) { | 141 | if (stage != previous_stage) { |
| 142 | ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage])); | 142 | ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage])); |
| @@ -160,7 +160,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size | |||
| 160 | // If theres a drastic slowdown in the rate, then display an estimate | 160 | // If theres a drastic slowdown in the rate, then display an estimate |
| 161 | if (now - previous_time > milliseconds{50} || slow_shader_compile_start) { | 161 | if (now - previous_time > milliseconds{50} || slow_shader_compile_start) { |
| 162 | if (!slow_shader_compile_start) { | 162 | if (!slow_shader_compile_start) { |
| 163 | slow_shader_start = high_resolution_clock::now(); | 163 | slow_shader_start = steady_clock::now(); |
| 164 | slow_shader_compile_start = true; | 164 | slow_shader_compile_start = true; |
| 165 | slow_shader_first_value = value; | 165 | slow_shader_first_value = value; |
| 166 | } | 166 | } |
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h index 801d08e1a..29155a77c 100644 --- a/src/yuzu/loading_screen.h +++ b/src/yuzu/loading_screen.h | |||
| @@ -84,8 +84,8 @@ private: | |||
| 84 | // shaders, it will start quickly but end slow if new shaders were added since previous launch. | 84 | // shaders, it will start quickly but end slow if new shaders were added since previous launch. |
| 85 | // These variables are used to detect the change in speed so we can generate an ETA | 85 | // These variables are used to detect the change in speed so we can generate an ETA |
| 86 | bool slow_shader_compile_start = false; | 86 | bool slow_shader_compile_start = false; |
| 87 | std::chrono::high_resolution_clock::time_point slow_shader_start; | 87 | std::chrono::steady_clock::time_point slow_shader_start; |
| 88 | std::chrono::high_resolution_clock::time_point previous_time; | 88 | std::chrono::steady_clock::time_point previous_time; |
| 89 | std::size_t slow_shader_first_value = 0; | 89 | std::size_t slow_shader_first_value = 0; |
| 90 | }; | 90 | }; |
| 91 | 91 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6071a222f..b7bb43348 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -26,6 +26,8 @@ | |||
| 26 | #include "core/frontend/applets/controller.h" | 26 | #include "core/frontend/applets/controller.h" |
| 27 | #include "core/frontend/applets/general_frontend.h" | 27 | #include "core/frontend/applets/general_frontend.h" |
| 28 | #include "core/frontend/applets/software_keyboard.h" | 28 | #include "core/frontend/applets/software_keyboard.h" |
| 29 | #include "core/hid/emulated_controller.h" | ||
| 30 | #include "core/hid/hid_core.h" | ||
| 29 | #include "core/hle/service/acc/profile_manager.h" | 31 | #include "core/hle/service/acc/profile_manager.h" |
| 30 | #include "core/hle/service/am/applet_ae.h" | 32 | #include "core/hle/service/am/applet_ae.h" |
| 31 | #include "core/hle/service/am/applet_oe.h" | 33 | #include "core/hle/service/am/applet_oe.h" |
| @@ -75,6 +77,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 75 | #include "common/fs/fs.h" | 77 | #include "common/fs/fs.h" |
| 76 | #include "common/fs/fs_paths.h" | 78 | #include "common/fs/fs_paths.h" |
| 77 | #include "common/fs/path_util.h" | 79 | #include "common/fs/path_util.h" |
| 80 | #include "common/literals.h" | ||
| 78 | #include "common/logging/backend.h" | 81 | #include "common/logging/backend.h" |
| 79 | #include "common/logging/filter.h" | 82 | #include "common/logging/filter.h" |
| 80 | #include "common/logging/log.h" | 83 | #include "common/logging/log.h" |
| @@ -106,8 +109,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 106 | #include "core/loader/loader.h" | 109 | #include "core/loader/loader.h" |
| 107 | #include "core/perf_stats.h" | 110 | #include "core/perf_stats.h" |
| 108 | #include "core/telemetry_session.h" | 111 | #include "core/telemetry_session.h" |
| 112 | #include "input_common/drivers/tas_input.h" | ||
| 109 | #include "input_common/main.h" | 113 | #include "input_common/main.h" |
| 110 | #include "input_common/tas/tas_input.h" | ||
| 111 | #include "ui_main.h" | 114 | #include "ui_main.h" |
| 112 | #include "util/overlay_dialog.h" | 115 | #include "util/overlay_dialog.h" |
| 113 | #include "video_core/gpu.h" | 116 | #include "video_core/gpu.h" |
| @@ -132,6 +135,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 132 | #include "yuzu/main.h" | 135 | #include "yuzu/main.h" |
| 133 | #include "yuzu/uisettings.h" | 136 | #include "yuzu/uisettings.h" |
| 134 | 137 | ||
| 138 | using namespace Common::Literals; | ||
| 139 | |||
| 135 | #ifdef USE_DISCORD_PRESENCE | 140 | #ifdef USE_DISCORD_PRESENCE |
| 136 | #include "yuzu/discord_impl.h" | 141 | #include "yuzu/discord_impl.h" |
| 137 | #endif | 142 | #endif |
| @@ -227,6 +232,9 @@ GMainWindow::GMainWindow() | |||
| 227 | ConnectMenuEvents(); | 232 | ConnectMenuEvents(); |
| 228 | ConnectWidgetEvents(); | 233 | ConnectWidgetEvents(); |
| 229 | 234 | ||
| 235 | system->HIDCore().ReloadInputDevices(); | ||
| 236 | controller_dialog->refreshConfiguration(); | ||
| 237 | |||
| 230 | const auto branch_name = std::string(Common::g_scm_branch); | 238 | const auto branch_name = std::string(Common::g_scm_branch); |
| 231 | const auto description = std::string(Common::g_scm_desc); | 239 | const auto description = std::string(Common::g_scm_desc); |
| 232 | const auto build_id = std::string(Common::g_build_id); | 240 | const auto build_id = std::string(Common::g_build_id); |
| @@ -254,10 +262,9 @@ GMainWindow::GMainWindow() | |||
| 254 | LOG_INFO(Frontend, "Host CPU: {}", cpu_string); | 262 | LOG_INFO(Frontend, "Host CPU: {}", cpu_string); |
| 255 | #endif | 263 | #endif |
| 256 | LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); | 264 | LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); |
| 257 | LOG_INFO(Frontend, "Host RAM: {:.2f} GB", | 265 | LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", |
| 258 | Common::GetMemInfo().TotalPhysicalMemory / 1024.0f / 1024 / 1024); | 266 | Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); |
| 259 | LOG_INFO(Frontend, "Host Swap: {:.2f} GB", | 267 | LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); |
| 260 | Common::GetMemInfo().TotalSwapMemory / 1024.0f / 1024 / 1024); | ||
| 261 | UpdateWindowTitle(); | 268 | UpdateWindowTitle(); |
| 262 | 269 | ||
| 263 | show(); | 270 | show(); |
| @@ -444,7 +451,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers( | |||
| 444 | } | 451 | } |
| 445 | 452 | ||
| 446 | void GMainWindow::ProfileSelectorSelectProfile() { | 453 | void GMainWindow::ProfileSelectorSelectProfile() { |
| 447 | QtProfileSelectionDialog dialog(this); | 454 | QtProfileSelectionDialog dialog(system->HIDCore(), this); |
| 448 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | | 455 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | |
| 449 | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | | 456 | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | |
| 450 | Qt::WindowCloseButtonHint); | 457 | Qt::WindowCloseButtonHint); |
| @@ -829,15 +836,16 @@ void GMainWindow::InitializeWidgets() { | |||
| 829 | dock_status_button->setFocusPolicy(Qt::NoFocus); | 836 | dock_status_button->setFocusPolicy(Qt::NoFocus); |
| 830 | connect(dock_status_button, &QPushButton::clicked, [&] { | 837 | connect(dock_status_button, &QPushButton::clicked, [&] { |
| 831 | const bool is_docked = Settings::values.use_docked_mode.GetValue(); | 838 | const bool is_docked = Settings::values.use_docked_mode.GetValue(); |
| 832 | auto& controller_type = Settings::values.players.GetValue()[0].controller_type; | 839 | auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 840 | auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 833 | 841 | ||
| 834 | if (!is_docked && controller_type == Settings::ControllerType::Handheld) { | 842 | if (!is_docked && handheld->IsConnected()) { |
| 835 | QMessageBox::warning(this, tr("Invalid config detected"), | 843 | QMessageBox::warning(this, tr("Invalid config detected"), |
| 836 | tr("Handheld controller can't be used on docked mode. Pro " | 844 | tr("Handheld controller can't be used on docked mode. Pro " |
| 837 | "controller will be selected.")); | 845 | "controller will be selected.")); |
| 838 | controller_type = Settings::ControllerType::ProController; | 846 | handheld->Disconnect(); |
| 839 | ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system); | 847 | player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); |
| 840 | configure_dialog.ApplyConfiguration(); | 848 | player_1->Connect(); |
| 841 | controller_dialog->refreshConfiguration(); | 849 | controller_dialog->refreshConfiguration(); |
| 842 | } | 850 | } |
| 843 | 851 | ||
| @@ -922,7 +930,7 @@ void GMainWindow::InitializeDebugWidgets() { | |||
| 922 | waitTreeWidget->hide(); | 930 | waitTreeWidget->hide(); |
| 923 | debug_menu->addAction(waitTreeWidget->toggleViewAction()); | 931 | debug_menu->addAction(waitTreeWidget->toggleViewAction()); |
| 924 | 932 | ||
| 925 | controller_dialog = new ControllerDialog(this, input_subsystem.get()); | 933 | controller_dialog = new ControllerDialog(system->HIDCore(), input_subsystem, this); |
| 926 | controller_dialog->hide(); | 934 | controller_dialog->hide(); |
| 927 | debug_menu->addAction(controller_dialog->toggleViewAction()); | 935 | debug_menu->addAction(controller_dialog->toggleViewAction()); |
| 928 | 936 | ||
| @@ -952,171 +960,80 @@ void GMainWindow::InitializeRecentFileMenuActions() { | |||
| 952 | UpdateRecentFiles(); | 960 | UpdateRecentFiles(); |
| 953 | } | 961 | } |
| 954 | 962 | ||
| 963 | void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name) { | ||
| 964 | static const QString main_window = QStringLiteral("Main Window"); | ||
| 965 | action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); | ||
| 966 | action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); | ||
| 967 | |||
| 968 | this->addAction(action); | ||
| 969 | } | ||
| 970 | |||
| 955 | void GMainWindow::InitializeHotkeys() { | 971 | void GMainWindow::InitializeHotkeys() { |
| 956 | hotkey_registry.LoadHotkeys(); | 972 | hotkey_registry.LoadHotkeys(); |
| 957 | 973 | ||
| 958 | const QString main_window = QStringLiteral("Main Window"); | 974 | LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); |
| 959 | const QString load_file = QStringLiteral("Load File"); | 975 | LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo")); |
| 960 | const QString load_amiibo = QStringLiteral("Load Amiibo"); | 976 | LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu")); |
| 961 | const QString exit_yuzu = QStringLiteral("Exit yuzu"); | 977 | LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); |
| 962 | const QString restart_emulation = QStringLiteral("Restart Emulation"); | 978 | LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); |
| 963 | const QString stop_emulation = QStringLiteral("Stop Emulation"); | 979 | LinkActionShortcut(ui->action_Stop, QStringLiteral("Stop Emulation")); |
| 964 | const QString toggle_filter_bar = QStringLiteral("Toggle Filter Bar"); | 980 | LinkActionShortcut(ui->action_Show_Filter_Bar, QStringLiteral("Toggle Filter Bar")); |
| 965 | const QString toggle_status_bar = QStringLiteral("Toggle Status Bar"); | 981 | LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar")); |
| 966 | const QString fullscreen = QStringLiteral("Fullscreen"); | 982 | LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen")); |
| 967 | const QString capture_screenshot = QStringLiteral("Capture Screenshot"); | 983 | LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot")); |
| 968 | 984 | LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop")); | |
| 969 | ui->action_Load_File->setShortcut(hotkey_registry.GetKeySequence(main_window, load_file)); | 985 | LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record")); |
| 970 | ui->action_Load_File->setShortcutContext( | 986 | LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset")); |
| 971 | hotkey_registry.GetShortcutContext(main_window, load_file)); | 987 | |
| 972 | 988 | static const QString main_window = QStringLiteral("Main Window"); | |
| 973 | ui->action_Load_Amiibo->setShortcut(hotkey_registry.GetKeySequence(main_window, load_amiibo)); | 989 | const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { |
| 974 | ui->action_Load_Amiibo->setShortcutContext( | 990 | const QShortcut* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); |
| 975 | hotkey_registry.GetShortcutContext(main_window, load_amiibo)); | 991 | connect(hotkey, &QShortcut::activated, this, function); |
| 976 | 992 | }; | |
| 977 | ui->action_Exit->setShortcut(hotkey_registry.GetKeySequence(main_window, exit_yuzu)); | 993 | |
| 978 | ui->action_Exit->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, exit_yuzu)); | 994 | connect_shortcut(QStringLiteral("Exit Fullscreen"), [&] { |
| 979 | 995 | if (emulation_running && ui->action_Fullscreen->isChecked()) { | |
| 980 | ui->action_Restart->setShortcut(hotkey_registry.GetKeySequence(main_window, restart_emulation)); | 996 | ui->action_Fullscreen->setChecked(false); |
| 981 | ui->action_Restart->setShortcutContext( | 997 | ToggleFullscreen(); |
| 982 | hotkey_registry.GetShortcutContext(main_window, restart_emulation)); | 998 | } |
| 983 | 999 | }); | |
| 984 | ui->action_Stop->setShortcut(hotkey_registry.GetKeySequence(main_window, stop_emulation)); | 1000 | connect_shortcut(QStringLiteral("Toggle Speed Limit"), [&] { |
| 985 | ui->action_Stop->setShortcutContext( | 1001 | Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue()); |
| 986 | hotkey_registry.GetShortcutContext(main_window, stop_emulation)); | 1002 | UpdateStatusBar(); |
| 987 | 1003 | }); | |
| 988 | ui->action_Show_Filter_Bar->setShortcut( | ||
| 989 | hotkey_registry.GetKeySequence(main_window, toggle_filter_bar)); | ||
| 990 | ui->action_Show_Filter_Bar->setShortcutContext( | ||
| 991 | hotkey_registry.GetShortcutContext(main_window, toggle_filter_bar)); | ||
| 992 | |||
| 993 | ui->action_Show_Status_Bar->setShortcut( | ||
| 994 | hotkey_registry.GetKeySequence(main_window, toggle_status_bar)); | ||
| 995 | ui->action_Show_Status_Bar->setShortcutContext( | ||
| 996 | hotkey_registry.GetShortcutContext(main_window, toggle_status_bar)); | ||
| 997 | |||
| 998 | ui->action_Capture_Screenshot->setShortcut( | ||
| 999 | hotkey_registry.GetKeySequence(main_window, capture_screenshot)); | ||
| 1000 | ui->action_Capture_Screenshot->setShortcutContext( | ||
| 1001 | hotkey_registry.GetShortcutContext(main_window, capture_screenshot)); | ||
| 1002 | |||
| 1003 | ui->action_Fullscreen->setShortcut( | ||
| 1004 | hotkey_registry.GetHotkey(main_window, fullscreen, this)->key()); | ||
| 1005 | ui->action_Fullscreen->setShortcutContext( | ||
| 1006 | hotkey_registry.GetShortcutContext(main_window, fullscreen)); | ||
| 1007 | |||
| 1008 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), | ||
| 1009 | &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); | ||
| 1010 | connect( | ||
| 1011 | hotkey_registry.GetHotkey(main_window, QStringLiteral("Continue/Pause Emulation"), this), | ||
| 1012 | &QShortcut::activated, this, [&] { | ||
| 1013 | if (emulation_running) { | ||
| 1014 | if (emu_thread->IsRunning()) { | ||
| 1015 | OnPauseGame(); | ||
| 1016 | } else { | ||
| 1017 | OnStartGame(); | ||
| 1018 | } | ||
| 1019 | } | ||
| 1020 | }); | ||
| 1021 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Restart Emulation"), this), | ||
| 1022 | &QShortcut::activated, this, [this] { | ||
| 1023 | if (!system->IsPoweredOn()) { | ||
| 1024 | return; | ||
| 1025 | } | ||
| 1026 | BootGame(game_path); | ||
| 1027 | }); | ||
| 1028 | connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window), | ||
| 1029 | &QShortcut::activated, ui->action_Fullscreen, &QAction::trigger); | ||
| 1030 | connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window), | ||
| 1031 | &QShortcut::activatedAmbiguously, ui->action_Fullscreen, &QAction::trigger); | ||
| 1032 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Exit Fullscreen"), this), | ||
| 1033 | &QShortcut::activated, this, [&] { | ||
| 1034 | if (emulation_running && ui->action_Fullscreen->isChecked()) { | ||
| 1035 | ui->action_Fullscreen->setChecked(false); | ||
| 1036 | ToggleFullscreen(); | ||
| 1037 | } | ||
| 1038 | }); | ||
| 1039 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this), | ||
| 1040 | &QShortcut::activated, this, [&] { | ||
| 1041 | Settings::values.use_speed_limit.SetValue( | ||
| 1042 | !Settings::values.use_speed_limit.GetValue()); | ||
| 1043 | UpdateStatusBar(); | ||
| 1044 | }); | ||
| 1045 | constexpr u16 SPEED_LIMIT_STEP = 5; | 1004 | constexpr u16 SPEED_LIMIT_STEP = 5; |
| 1046 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), | 1005 | connect_shortcut(QStringLiteral("Increase Speed Limit"), [&] { |
| 1047 | &QShortcut::activated, this, [&] { | 1006 | if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) { |
| 1048 | if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) { | 1007 | Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP + |
| 1049 | Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP + | 1008 | Settings::values.speed_limit.GetValue()); |
| 1050 | Settings::values.speed_limit.GetValue()); | 1009 | UpdateStatusBar(); |
| 1051 | UpdateStatusBar(); | 1010 | } |
| 1052 | } | 1011 | }); |
| 1053 | }); | 1012 | connect_shortcut(QStringLiteral("Decrease Speed Limit"), [&] { |
| 1054 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), | 1013 | if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) { |
| 1055 | &QShortcut::activated, this, [&] { | 1014 | Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() - |
| 1056 | if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) { | 1015 | SPEED_LIMIT_STEP); |
| 1057 | Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() - | 1016 | UpdateStatusBar(); |
| 1058 | SPEED_LIMIT_STEP); | 1017 | } |
| 1059 | UpdateStatusBar(); | 1018 | }); |
| 1060 | } | 1019 | connect_shortcut(QStringLiteral("Change Docked Mode"), [&] { |
| 1061 | }); | 1020 | Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue()); |
| 1062 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load Amiibo"), this), | 1021 | OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), |
| 1063 | &QShortcut::activated, this, [&] { | 1022 | Settings::values.use_docked_mode.GetValue(), *system); |
| 1064 | if (ui->action_Load_Amiibo->isEnabled()) { | 1023 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); |
| 1065 | OnLoadAmiibo(); | 1024 | }); |
| 1066 | } | 1025 | connect_shortcut(QStringLiteral("Mute Audio"), |
| 1067 | }); | 1026 | [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); |
| 1068 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), | 1027 | connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] { |
| 1069 | &QShortcut::activated, this, [&] { | 1028 | Settings::values.disable_fps_limit.SetValue(!Settings::values.disable_fps_limit.GetValue()); |
| 1070 | if (emu_thread != nullptr && emu_thread->IsRunning()) { | 1029 | }); |
| 1071 | OnCaptureScreenshot(); | 1030 | connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] { |
| 1072 | } | 1031 | Settings::values.mouse_panning = !Settings::values.mouse_panning; |
| 1073 | }); | 1032 | if (Settings::values.mouse_panning) { |
| 1074 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this), | 1033 | render_window->installEventFilter(render_window); |
| 1075 | &QShortcut::activated, this, [&] { | 1034 | render_window->setAttribute(Qt::WA_Hover, true); |
| 1076 | Settings::values.use_docked_mode.SetValue( | 1035 | } |
| 1077 | !Settings::values.use_docked_mode.GetValue()); | 1036 | }); |
| 1078 | OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), | ||
| 1079 | Settings::values.use_docked_mode.GetValue(), *system); | ||
| 1080 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); | ||
| 1081 | }); | ||
| 1082 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), | ||
| 1083 | &QShortcut::activated, this, | ||
| 1084 | [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); | ||
| 1085 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Framerate Limit"), this), | ||
| 1086 | &QShortcut::activated, this, [] { | ||
| 1087 | Settings::values.disable_fps_limit.SetValue( | ||
| 1088 | !Settings::values.disable_fps_limit.GetValue()); | ||
| 1089 | }); | ||
| 1090 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this), | ||
| 1091 | &QShortcut::activated, this, [&] { | ||
| 1092 | Settings::values.mouse_panning = !Settings::values.mouse_panning; | ||
| 1093 | if (Settings::values.mouse_panning) { | ||
| 1094 | render_window->installEventFilter(render_window); | ||
| 1095 | render_window->setAttribute(Qt::WA_Hover, true); | ||
| 1096 | } | ||
| 1097 | }); | ||
| 1098 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Start/Stop"), this), | ||
| 1099 | &QShortcut::activated, this, [&] { | ||
| 1100 | if (!emulation_running) { | ||
| 1101 | return; | ||
| 1102 | } | ||
| 1103 | input_subsystem->GetTas()->StartStop(); | ||
| 1104 | }); | ||
| 1105 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Reset"), this), | ||
| 1106 | &QShortcut::activated, this, [&] { input_subsystem->GetTas()->Reset(); }); | ||
| 1107 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Record"), this), | ||
| 1108 | &QShortcut::activated, this, [&] { | ||
| 1109 | if (!emulation_running) { | ||
| 1110 | return; | ||
| 1111 | } | ||
| 1112 | bool is_recording = input_subsystem->GetTas()->Record(); | ||
| 1113 | if (!is_recording) { | ||
| 1114 | const auto res = QMessageBox::question(this, tr("TAS Recording"), | ||
| 1115 | tr("Overwrite file of player 1?"), | ||
| 1116 | QMessageBox::Yes | QMessageBox::No); | ||
| 1117 | input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes); | ||
| 1118 | } | ||
| 1119 | }); | ||
| 1120 | } | 1037 | } |
| 1121 | 1038 | ||
| 1122 | void GMainWindow::SetDefaultUIGeometry() { | 1039 | void GMainWindow::SetDefaultUIGeometry() { |
| @@ -1167,13 +1084,15 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { | |||
| 1167 | state != Qt::ApplicationActive) { | 1084 | state != Qt::ApplicationActive) { |
| 1168 | LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); | 1085 | LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); |
| 1169 | } | 1086 | } |
| 1170 | if (ui->action_Pause->isEnabled() && | 1087 | if (emulation_running) { |
| 1171 | (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { | 1088 | if (emu_thread->IsRunning() && |
| 1172 | auto_paused = true; | 1089 | (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { |
| 1173 | OnPauseGame(); | 1090 | auto_paused = true; |
| 1174 | } else if (ui->action_Start->isEnabled() && auto_paused && state == Qt::ApplicationActive) { | 1091 | OnPauseGame(); |
| 1175 | auto_paused = false; | 1092 | } else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) { |
| 1176 | OnStartGame(); | 1093 | auto_paused = false; |
| 1094 | OnStartGame(); | ||
| 1095 | } | ||
| 1177 | } | 1096 | } |
| 1178 | } | 1097 | } |
| 1179 | 1098 | ||
| @@ -1215,61 +1134,86 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 1215 | } | 1134 | } |
| 1216 | 1135 | ||
| 1217 | void GMainWindow::ConnectMenuEvents() { | 1136 | void GMainWindow::ConnectMenuEvents() { |
| 1137 | const auto connect_menu = [&]<typename Fn>(QAction* action, const Fn& event_fn) { | ||
| 1138 | connect(action, &QAction::triggered, this, event_fn); | ||
| 1139 | // Add actions to this window so that hiding menus in fullscreen won't disable them | ||
| 1140 | addAction(action); | ||
| 1141 | // Add actions to the render window so that they work outside of single window mode | ||
| 1142 | render_window->addAction(action); | ||
| 1143 | }; | ||
| 1144 | |||
| 1218 | // File | 1145 | // File |
| 1219 | connect(ui->action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); | 1146 | connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile); |
| 1220 | connect(ui->action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder); | 1147 | connect_menu(ui->action_Load_Folder, &GMainWindow::OnMenuLoadFolder); |
| 1221 | connect(ui->action_Install_File_NAND, &QAction::triggered, this, | 1148 | connect_menu(ui->action_Install_File_NAND, &GMainWindow::OnMenuInstallToNAND); |
| 1222 | &GMainWindow::OnMenuInstallToNAND); | 1149 | connect_menu(ui->action_Exit, &QMainWindow::close); |
| 1223 | connect(ui->action_Exit, &QAction::triggered, this, &QMainWindow::close); | 1150 | connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo); |
| 1224 | connect(ui->action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo); | ||
| 1225 | 1151 | ||
| 1226 | // Emulation | 1152 | // Emulation |
| 1227 | connect(ui->action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); | 1153 | connect_menu(ui->action_Pause, &GMainWindow::OnPauseContinueGame); |
| 1228 | connect(ui->action_Pause, &QAction::triggered, this, &GMainWindow::OnPauseGame); | 1154 | connect_menu(ui->action_Stop, &GMainWindow::OnStopGame); |
| 1229 | connect(ui->action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); | 1155 | connect_menu(ui->action_Report_Compatibility, &GMainWindow::OnMenuReportCompatibility); |
| 1230 | connect(ui->action_Report_Compatibility, &QAction::triggered, this, | 1156 | connect_menu(ui->action_Open_Mods_Page, &GMainWindow::OnOpenModsPage); |
| 1231 | &GMainWindow::OnMenuReportCompatibility); | 1157 | connect_menu(ui->action_Open_Quickstart_Guide, &GMainWindow::OnOpenQuickstartGuide); |
| 1232 | connect(ui->action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); | 1158 | connect_menu(ui->action_Open_FAQ, &GMainWindow::OnOpenFAQ); |
| 1233 | connect(ui->action_Open_Quickstart_Guide, &QAction::triggered, this, | 1159 | connect_menu(ui->action_Restart, &GMainWindow::OnRestartGame); |
| 1234 | &GMainWindow::OnOpenQuickstartGuide); | 1160 | connect_menu(ui->action_Configure, &GMainWindow::OnConfigure); |
| 1235 | connect(ui->action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ); | 1161 | connect_menu(ui->action_Configure_Current_Game, &GMainWindow::OnConfigurePerGame); |
| 1236 | connect(ui->action_Restart, &QAction::triggered, this, | ||
| 1237 | [this] { BootGame(QString(game_path)); }); | ||
| 1238 | connect(ui->action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); | ||
| 1239 | connect(ui->action_Configure_Tas, &QAction::triggered, this, &GMainWindow::OnConfigureTas); | ||
| 1240 | connect(ui->action_Configure_Current_Game, &QAction::triggered, this, | ||
| 1241 | &GMainWindow::OnConfigurePerGame); | ||
| 1242 | 1162 | ||
| 1243 | // View | 1163 | // View |
| 1244 | connect(ui->action_Single_Window_Mode, &QAction::triggered, this, | 1164 | connect_menu(ui->action_Fullscreen, &GMainWindow::ToggleFullscreen); |
| 1245 | &GMainWindow::ToggleWindowMode); | 1165 | connect_menu(ui->action_Single_Window_Mode, &GMainWindow::ToggleWindowMode); |
| 1246 | connect(ui->action_Display_Dock_Widget_Headers, &QAction::triggered, this, | 1166 | connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars); |
| 1247 | &GMainWindow::OnDisplayTitleBars); | 1167 | connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar); |
| 1248 | connect(ui->action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); | 1168 | |
| 1249 | connect(ui->action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); | 1169 | connect(ui->action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); |
| 1250 | 1170 | ||
| 1251 | connect(ui->action_Reset_Window_Size_720, &QAction::triggered, this, | 1171 | connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720); |
| 1252 | &GMainWindow::ResetWindowSize720); | 1172 | connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900); |
| 1253 | connect(ui->action_Reset_Window_Size_900, &QAction::triggered, this, | 1173 | connect_menu(ui->action_Reset_Window_Size_1080, &GMainWindow::ResetWindowSize1080); |
| 1254 | &GMainWindow::ResetWindowSize900); | 1174 | ui->menu_Reset_Window_Size->addActions({ui->action_Reset_Window_Size_720, |
| 1255 | connect(ui->action_Reset_Window_Size_1080, &QAction::triggered, this, | 1175 | ui->action_Reset_Window_Size_900, |
| 1256 | &GMainWindow::ResetWindowSize1080); | 1176 | ui->action_Reset_Window_Size_1080}); |
| 1257 | ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_720); | ||
| 1258 | ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_900); | ||
| 1259 | ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_1080); | ||
| 1260 | 1177 | ||
| 1261 | // Fullscreen | 1178 | // Tools |
| 1262 | connect(ui->action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); | 1179 | connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, |
| 1180 | ReinitializeKeyBehavior::Warning)); | ||
| 1181 | connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); | ||
| 1263 | 1182 | ||
| 1264 | // Movie | 1183 | // TAS |
| 1265 | connect(ui->action_Capture_Screenshot, &QAction::triggered, this, | 1184 | connect_menu(ui->action_TAS_Start, &GMainWindow::OnTasStartStop); |
| 1266 | &GMainWindow::OnCaptureScreenshot); | 1185 | connect_menu(ui->action_TAS_Record, &GMainWindow::OnTasRecord); |
| 1186 | connect_menu(ui->action_TAS_Reset, &GMainWindow::OnTasReset); | ||
| 1187 | connect_menu(ui->action_Configure_Tas, &GMainWindow::OnConfigureTas); | ||
| 1267 | 1188 | ||
| 1268 | // Help | 1189 | // Help |
| 1269 | connect(ui->action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder); | 1190 | connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); |
| 1270 | connect(ui->action_Rederive, &QAction::triggered, this, | 1191 | connect_menu(ui->action_About, &GMainWindow::OnAbout); |
| 1271 | std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); | 1192 | } |
| 1272 | connect(ui->action_About, &QAction::triggered, this, &GMainWindow::OnAbout); | 1193 | |
| 1194 | void GMainWindow::UpdateMenuState() { | ||
| 1195 | const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning(); | ||
| 1196 | |||
| 1197 | const std::array running_actions{ | ||
| 1198 | ui->action_Stop, | ||
| 1199 | ui->action_Restart, | ||
| 1200 | ui->action_Configure_Current_Game, | ||
| 1201 | ui->action_Report_Compatibility, | ||
| 1202 | ui->action_Load_Amiibo, | ||
| 1203 | ui->action_Pause, | ||
| 1204 | }; | ||
| 1205 | |||
| 1206 | for (QAction* action : running_actions) { | ||
| 1207 | action->setEnabled(emulation_running); | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | ui->action_Capture_Screenshot->setEnabled(emulation_running && !is_paused); | ||
| 1211 | |||
| 1212 | if (emulation_running && is_paused) { | ||
| 1213 | ui->action_Pause->setText(tr("&Continue")); | ||
| 1214 | } else { | ||
| 1215 | ui->action_Pause->setText(tr("&Pause")); | ||
| 1216 | } | ||
| 1273 | } | 1217 | } |
| 1274 | 1218 | ||
| 1275 | void GMainWindow::OnDisplayTitleBars(bool show) { | 1219 | void GMainWindow::OnDisplayTitleBars(bool show) { |
| @@ -1360,16 +1304,13 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p | |||
| 1360 | case Core::SystemResultStatus::ErrorVideoCore: | 1304 | case Core::SystemResultStatus::ErrorVideoCore: |
| 1361 | QMessageBox::critical( | 1305 | QMessageBox::critical( |
| 1362 | this, tr("An error occurred initializing the video core."), | 1306 | this, tr("An error occurred initializing the video core."), |
| 1363 | tr("yuzu has encountered an error while running the video core, please see the " | 1307 | tr("yuzu has encountered an error while running the video core. " |
| 1364 | "log for more details." | 1308 | "This is usually caused by outdated GPU drivers, including integrated ones. " |
| 1309 | "Please see the log for more details. " | ||
| 1365 | "For more information on accessing the log, please see the following page: " | 1310 | "For more information on accessing the log, please see the following page: " |
| 1366 | "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " | 1311 | "<a href='https://yuzu-emu.org/help/reference/log-files/'>" |
| 1367 | "to " | 1312 | "How to Upload the Log File</a>. ")); |
| 1368 | "Upload the Log File</a>." | ||
| 1369 | "Ensure that you have the latest graphics drivers for your GPU.")); | ||
| 1370 | |||
| 1371 | break; | 1313 | break; |
| 1372 | |||
| 1373 | default: | 1314 | default: |
| 1374 | if (result > Core::SystemResultStatus::ErrorLoader) { | 1315 | if (result > Core::SystemResultStatus::ErrorLoader) { |
| 1375 | const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader); | 1316 | const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader); |
| @@ -1405,7 +1346,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p | |||
| 1405 | } | 1346 | } |
| 1406 | 1347 | ||
| 1407 | void GMainWindow::SelectAndSetCurrentUser() { | 1348 | void GMainWindow::SelectAndSetCurrentUser() { |
| 1408 | QtProfileSelectionDialog dialog(this); | 1349 | QtProfileSelectionDialog dialog(system->HIDCore(), this); |
| 1409 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | | 1350 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | |
| 1410 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); | 1351 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); |
| 1411 | dialog.setWindowModality(Qt::WindowModal); | 1352 | dialog.setWindowModality(Qt::WindowModal); |
| @@ -1439,8 +1380,6 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1439 | Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig); | 1380 | Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig); |
| 1440 | } | 1381 | } |
| 1441 | 1382 | ||
| 1442 | ConfigureVibration::SetAllVibrationDevices(); | ||
| 1443 | |||
| 1444 | // Disable fps limit toggle when booting a new title | 1383 | // Disable fps limit toggle when booting a new title |
| 1445 | Settings::values.disable_fps_limit.SetValue(false); | 1384 | Settings::values.disable_fps_limit.SetValue(false); |
| 1446 | 1385 | ||
| @@ -1562,15 +1501,8 @@ void GMainWindow::ShutdownGame() { | |||
| 1562 | disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 1501 | disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| 1563 | 1502 | ||
| 1564 | // Update the GUI | 1503 | // Update the GUI |
| 1565 | ui->action_Start->setEnabled(false); | 1504 | UpdateMenuState(); |
| 1566 | ui->action_Start->setText(tr("Start")); | 1505 | |
| 1567 | ui->action_Pause->setEnabled(false); | ||
| 1568 | ui->action_Stop->setEnabled(false); | ||
| 1569 | ui->action_Restart->setEnabled(false); | ||
| 1570 | ui->action_Configure_Current_Game->setEnabled(false); | ||
| 1571 | ui->action_Report_Compatibility->setEnabled(false); | ||
| 1572 | ui->action_Load_Amiibo->setEnabled(false); | ||
| 1573 | ui->action_Capture_Screenshot->setEnabled(false); | ||
| 1574 | render_window->hide(); | 1506 | render_window->hide(); |
| 1575 | loading_screen->hide(); | 1507 | loading_screen->hide(); |
| 1576 | loading_screen->Clear(); | 1508 | loading_screen->Clear(); |
| @@ -1582,6 +1514,10 @@ void GMainWindow::ShutdownGame() { | |||
| 1582 | game_list->SetFilterFocus(); | 1514 | game_list->SetFilterFocus(); |
| 1583 | tas_label->clear(); | 1515 | tas_label->clear(); |
| 1584 | input_subsystem->GetTas()->Stop(); | 1516 | input_subsystem->GetTas()->Stop(); |
| 1517 | OnTasStateChanged(); | ||
| 1518 | |||
| 1519 | // Enable all controllers | ||
| 1520 | system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); | ||
| 1585 | 1521 | ||
| 1586 | render_window->removeEventFilter(render_window); | 1522 | render_window->removeEventFilter(render_window); |
| 1587 | render_window->setAttribute(Qt::WA_Hover, false); | 1523 | render_window->setAttribute(Qt::WA_Hover, false); |
| @@ -1675,7 +1611,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1675 | if (has_user_save) { | 1611 | if (has_user_save) { |
| 1676 | // User save data | 1612 | // User save data |
| 1677 | const auto select_profile = [this] { | 1613 | const auto select_profile = [this] { |
| 1678 | QtProfileSelectionDialog dialog(this); | 1614 | QtProfileSelectionDialog dialog(system->HIDCore(), this); |
| 1679 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | | 1615 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | |
| 1680 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); | 1616 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); |
| 1681 | dialog.setWindowModality(Qt::WindowModal); | 1617 | dialog.setWindowModality(Qt::WindowModal); |
| @@ -2501,31 +2437,36 @@ void GMainWindow::OnStartGame() { | |||
| 2501 | 2437 | ||
| 2502 | connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); | 2438 | connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); |
| 2503 | 2439 | ||
| 2504 | ui->action_Start->setEnabled(false); | 2440 | UpdateMenuState(); |
| 2505 | ui->action_Start->setText(tr("&Continue")); | 2441 | OnTasStateChanged(); |
| 2506 | |||
| 2507 | ui->action_Pause->setEnabled(true); | ||
| 2508 | ui->action_Stop->setEnabled(true); | ||
| 2509 | ui->action_Restart->setEnabled(true); | ||
| 2510 | ui->action_Configure_Current_Game->setEnabled(true); | ||
| 2511 | ui->action_Report_Compatibility->setEnabled(true); | ||
| 2512 | 2442 | ||
| 2513 | discord_rpc->Update(); | 2443 | discord_rpc->Update(); |
| 2514 | ui->action_Load_Amiibo->setEnabled(true); | 2444 | } |
| 2515 | ui->action_Capture_Screenshot->setEnabled(true); | 2445 | |
| 2446 | void GMainWindow::OnRestartGame() { | ||
| 2447 | if (!system->IsPoweredOn()) { | ||
| 2448 | return; | ||
| 2449 | } | ||
| 2450 | // Make a copy since BootGame edits game_path | ||
| 2451 | BootGame(QString(game_path)); | ||
| 2516 | } | 2452 | } |
| 2517 | 2453 | ||
| 2518 | void GMainWindow::OnPauseGame() { | 2454 | void GMainWindow::OnPauseGame() { |
| 2519 | emu_thread->SetRunning(false); | 2455 | emu_thread->SetRunning(false); |
| 2520 | 2456 | UpdateMenuState(); | |
| 2521 | ui->action_Start->setEnabled(true); | ||
| 2522 | ui->action_Pause->setEnabled(false); | ||
| 2523 | ui->action_Stop->setEnabled(true); | ||
| 2524 | ui->action_Capture_Screenshot->setEnabled(false); | ||
| 2525 | |||
| 2526 | AllowOSSleep(); | 2457 | AllowOSSleep(); |
| 2527 | } | 2458 | } |
| 2528 | 2459 | ||
| 2460 | void GMainWindow::OnPauseContinueGame() { | ||
| 2461 | if (emulation_running) { | ||
| 2462 | if (emu_thread->IsRunning()) { | ||
| 2463 | OnPauseGame(); | ||
| 2464 | } else { | ||
| 2465 | OnStartGame(); | ||
| 2466 | } | ||
| 2467 | } | ||
| 2468 | } | ||
| 2469 | |||
| 2529 | void GMainWindow::OnStopGame() { | 2470 | void GMainWindow::OnStopGame() { |
| 2530 | if (system->GetExitLock() && !ConfirmForceLockedExit()) { | 2471 | if (system->GetExitLock() && !ConfirmForceLockedExit()) { |
| 2531 | return; | 2472 | return; |
| @@ -2774,7 +2715,6 @@ void GMainWindow::OnConfigure() { | |||
| 2774 | 2715 | ||
| 2775 | ShowTelemetryCallout(); | 2716 | ShowTelemetryCallout(); |
| 2776 | } | 2717 | } |
| 2777 | controller_dialog->refreshConfiguration(); | ||
| 2778 | InitializeHotkeys(); | 2718 | InitializeHotkeys(); |
| 2779 | 2719 | ||
| 2780 | if (UISettings::values.theme != old_theme) { | 2720 | if (UISettings::values.theme != old_theme) { |
| @@ -2807,6 +2747,7 @@ void GMainWindow::OnConfigure() { | |||
| 2807 | } | 2747 | } |
| 2808 | 2748 | ||
| 2809 | UpdateStatusButtons(); | 2749 | UpdateStatusButtons(); |
| 2750 | controller_dialog->refreshConfiguration(); | ||
| 2810 | } | 2751 | } |
| 2811 | 2752 | ||
| 2812 | void GMainWindow::OnConfigureTas() { | 2753 | void GMainWindow::OnConfigureTas() { |
| @@ -2821,6 +2762,32 @@ void GMainWindow::OnConfigureTas() { | |||
| 2821 | } | 2762 | } |
| 2822 | } | 2763 | } |
| 2823 | 2764 | ||
| 2765 | void GMainWindow::OnTasStartStop() { | ||
| 2766 | if (!emulation_running) { | ||
| 2767 | return; | ||
| 2768 | } | ||
| 2769 | input_subsystem->GetTas()->StartStop(); | ||
| 2770 | OnTasStateChanged(); | ||
| 2771 | } | ||
| 2772 | |||
| 2773 | void GMainWindow::OnTasRecord() { | ||
| 2774 | if (!emulation_running) { | ||
| 2775 | return; | ||
| 2776 | } | ||
| 2777 | const bool is_recording = input_subsystem->GetTas()->Record(); | ||
| 2778 | if (!is_recording) { | ||
| 2779 | const auto res = | ||
| 2780 | QMessageBox::question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"), | ||
| 2781 | QMessageBox::Yes | QMessageBox::No); | ||
| 2782 | input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes); | ||
| 2783 | } | ||
| 2784 | OnTasStateChanged(); | ||
| 2785 | } | ||
| 2786 | |||
| 2787 | void GMainWindow::OnTasReset() { | ||
| 2788 | input_subsystem->GetTas()->Reset(); | ||
| 2789 | } | ||
| 2790 | |||
| 2824 | void GMainWindow::OnConfigurePerGame() { | 2791 | void GMainWindow::OnConfigurePerGame() { |
| 2825 | const u64 title_id = system->GetCurrentProcessProgramID(); | 2792 | const u64 title_id = system->GetCurrentProcessProgramID(); |
| 2826 | OpenPerGameConfiguration(title_id, game_path.toStdString()); | 2793 | OpenPerGameConfiguration(title_id, game_path.toStdString()); |
| @@ -2858,6 +2825,10 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file | |||
| 2858 | } | 2825 | } |
| 2859 | 2826 | ||
| 2860 | void GMainWindow::OnLoadAmiibo() { | 2827 | void GMainWindow::OnLoadAmiibo() { |
| 2828 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { | ||
| 2829 | return; | ||
| 2830 | } | ||
| 2831 | |||
| 2861 | const QString extensions{QStringLiteral("*.bin")}; | 2832 | const QString extensions{QStringLiteral("*.bin")}; |
| 2862 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | 2833 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); |
| 2863 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); | 2834 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); |
| @@ -2921,6 +2892,10 @@ void GMainWindow::OnToggleFilterBar() { | |||
| 2921 | } | 2892 | } |
| 2922 | 2893 | ||
| 2923 | void GMainWindow::OnCaptureScreenshot() { | 2894 | void GMainWindow::OnCaptureScreenshot() { |
| 2895 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { | ||
| 2896 | return; | ||
| 2897 | } | ||
| 2898 | |||
| 2924 | const u64 title_id = system->GetCurrentProcessProgramID(); | 2899 | const u64 title_id = system->GetCurrentProcessProgramID(); |
| 2925 | const auto screenshot_path = | 2900 | const auto screenshot_path = |
| 2926 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); | 2901 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); |
| @@ -3003,17 +2978,35 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie | |||
| 3003 | QString GMainWindow::GetTasStateDescription() const { | 2978 | QString GMainWindow::GetTasStateDescription() const { |
| 3004 | auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus(); | 2979 | auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus(); |
| 3005 | switch (tas_status) { | 2980 | switch (tas_status) { |
| 3006 | case TasInput::TasState::Running: | 2981 | case InputCommon::TasInput::TasState::Running: |
| 3007 | return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames); | 2982 | return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames); |
| 3008 | case TasInput::TasState::Recording: | 2983 | case InputCommon::TasInput::TasState::Recording: |
| 3009 | return tr("TAS state: Recording %1").arg(total_tas_frames); | 2984 | return tr("TAS state: Recording %1").arg(total_tas_frames); |
| 3010 | case TasInput::TasState::Stopped: | 2985 | case InputCommon::TasInput::TasState::Stopped: |
| 3011 | return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames); | 2986 | return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames); |
| 3012 | default: | 2987 | default: |
| 3013 | return tr("TAS State: Invalid"); | 2988 | return tr("TAS State: Invalid"); |
| 3014 | } | 2989 | } |
| 3015 | } | 2990 | } |
| 3016 | 2991 | ||
| 2992 | void GMainWindow::OnTasStateChanged() { | ||
| 2993 | bool is_running = false; | ||
| 2994 | bool is_recording = false; | ||
| 2995 | if (emulation_running) { | ||
| 2996 | const InputCommon::TasInput::TasState tas_status = | ||
| 2997 | std::get<0>(input_subsystem->GetTas()->GetStatus()); | ||
| 2998 | is_running = tas_status == InputCommon::TasInput::TasState::Running; | ||
| 2999 | is_recording = tas_status == InputCommon::TasInput::TasState::Recording; | ||
| 3000 | } | ||
| 3001 | |||
| 3002 | ui->action_TAS_Start->setText(is_running ? tr("&Stop Running") : tr("&Start")); | ||
| 3003 | ui->action_TAS_Record->setText(is_recording ? tr("Stop R&ecording") : tr("R&ecord")); | ||
| 3004 | |||
| 3005 | ui->action_TAS_Start->setEnabled(emulation_running); | ||
| 3006 | ui->action_TAS_Record->setEnabled(emulation_running); | ||
| 3007 | ui->action_TAS_Reset->setEnabled(emulation_running); | ||
| 3008 | } | ||
| 3009 | |||
| 3017 | void GMainWindow::UpdateStatusBar() { | 3010 | void GMainWindow::UpdateStatusBar() { |
| 3018 | if (emu_thread == nullptr) { | 3011 | if (emu_thread == nullptr) { |
| 3019 | status_bar_update_timer.stop(); | 3012 | status_bar_update_timer.stop(); |
| @@ -3106,7 +3099,7 @@ void GMainWindow::UpdateFilterText() { | |||
| 3106 | filter_status_button->setText(tr("SCALEFORCE")); | 3099 | filter_status_button->setText(tr("SCALEFORCE")); |
| 3107 | break; | 3100 | break; |
| 3108 | case Settings::ScalingFilter::Fsr: | 3101 | case Settings::ScalingFilter::Fsr: |
| 3109 | filter_status_button->setText(tr("AMD'S FIDELITYFX SR")); | 3102 | filter_status_button->setText(tr("FSR")); |
| 3110 | break; | 3103 | break; |
| 3111 | default: | 3104 | default: |
| 3112 | filter_status_button->setText(tr("BILINEAR")); | 3105 | filter_status_button->setText(tr("BILINEAR")); |
| @@ -3117,15 +3110,15 @@ void GMainWindow::UpdateFilterText() { | |||
| 3117 | void GMainWindow::UpdateAAText() { | 3110 | void GMainWindow::UpdateAAText() { |
| 3118 | const auto aa_mode = Settings::values.anti_aliasing.GetValue(); | 3111 | const auto aa_mode = Settings::values.anti_aliasing.GetValue(); |
| 3119 | switch (aa_mode) { | 3112 | switch (aa_mode) { |
| 3120 | case Settings::AntiAliasing::Fxaa: | ||
| 3121 | aa_status_button->setText(tr("FXAA")); | ||
| 3122 | break; | ||
| 3123 | case Settings::AntiAliasing::None: | 3113 | case Settings::AntiAliasing::None: |
| 3124 | aa_status_button->setText(tr("NO AA")); | 3114 | aa_status_button->setText(tr("NO AA")); |
| 3125 | break; | 3115 | break; |
| 3126 | default: | 3116 | case Settings::AntiAliasing::Fxaa: |
| 3127 | aa_status_button->setText(tr("FXAA")); | 3117 | aa_status_button->setText(tr("FXAA")); |
| 3128 | break; | 3118 | break; |
| 3119 | default: | ||
| 3120 | aa_status_button->setText(tr("NO AA")); | ||
| 3121 | break; | ||
| 3129 | } | 3122 | } |
| 3130 | } | 3123 | } |
| 3131 | 3124 | ||
| @@ -3300,9 +3293,9 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 3300 | if (!errors.isEmpty()) { | 3293 | if (!errors.isEmpty()) { |
| 3301 | QMessageBox::warning( | 3294 | QMessageBox::warning( |
| 3302 | this, tr("Derivation Components Missing"), | 3295 | this, tr("Derivation Components Missing"), |
| 3303 | tr("Components are missing that may hinder key derivation from completing. " | 3296 | tr("Encryption keys are missing. " |
| 3304 | "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu " | 3297 | "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu " |
| 3305 | "quickstart guide</a> to get all your keys and " | 3298 | "quickstart guide</a> to get all your keys, firmware and " |
| 3306 | "games.<br><br><small>(%1)</small>") | 3299 | "games.<br><br><small>(%1)</small>") |
| 3307 | .arg(errors)); | 3300 | .arg(errors)); |
| 3308 | } | 3301 | } |
| @@ -3387,6 +3380,11 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 3387 | game_list->SaveInterfaceLayout(); | 3380 | game_list->SaveInterfaceLayout(); |
| 3388 | hotkey_registry.SaveHotkeys(); | 3381 | hotkey_registry.SaveHotkeys(); |
| 3389 | 3382 | ||
| 3383 | // Unload controllers early | ||
| 3384 | controller_dialog->UnloadController(); | ||
| 3385 | game_list->UnloadController(); | ||
| 3386 | system->HIDCore().UnloadInputDevices(); | ||
| 3387 | |||
| 3390 | // Shutdown session if the emu thread is active... | 3388 | // Shutdown session if the emu thread is active... |
| 3391 | if (emu_thread != nullptr) { | 3389 | if (emu_thread != nullptr) { |
| 3392 | ShutdownGame(); | 3390 | ShutdownGame(); |
| @@ -3487,36 +3485,38 @@ void GMainWindow::filterBarSetChecked(bool state) { | |||
| 3487 | } | 3485 | } |
| 3488 | 3486 | ||
| 3489 | void GMainWindow::UpdateUITheme() { | 3487 | void GMainWindow::UpdateUITheme() { |
| 3490 | const QString default_icons = QStringLiteral("default"); | 3488 | const QString default_theme = QStringLiteral("default"); |
| 3491 | const QString& current_theme = UISettings::values.theme; | 3489 | QString current_theme = UISettings::values.theme; |
| 3492 | const bool is_default_theme = current_theme == QString::fromUtf8(UISettings::themes[0].second); | ||
| 3493 | QStringList theme_paths(default_theme_paths); | 3490 | QStringList theme_paths(default_theme_paths); |
| 3494 | 3491 | ||
| 3495 | if (is_default_theme || current_theme.isEmpty()) { | 3492 | if (current_theme.isEmpty()) { |
| 3496 | const QString theme_uri(QStringLiteral(":default/style.qss")); | 3493 | current_theme = default_theme; |
| 3494 | } | ||
| 3495 | |||
| 3496 | if (current_theme != default_theme) { | ||
| 3497 | QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)}; | ||
| 3497 | QFile f(theme_uri); | 3498 | QFile f(theme_uri); |
| 3498 | if (f.open(QFile::ReadOnly | QFile::Text)) { | 3499 | if (!f.open(QFile::ReadOnly | QFile::Text)) { |
| 3499 | QTextStream ts(&f); | 3500 | LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", |
| 3500 | qApp->setStyleSheet(ts.readAll()); | 3501 | UISettings::values.theme.toStdString()); |
| 3501 | setStyleSheet(ts.readAll()); | 3502 | current_theme = default_theme; |
| 3502 | } else { | ||
| 3503 | qApp->setStyleSheet({}); | ||
| 3504 | setStyleSheet({}); | ||
| 3505 | } | 3503 | } |
| 3506 | QIcon::setThemeName(default_icons); | 3504 | } |
| 3505 | |||
| 3506 | QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)}; | ||
| 3507 | QFile f(theme_uri); | ||
| 3508 | if (f.open(QFile::ReadOnly | QFile::Text)) { | ||
| 3509 | QTextStream ts(&f); | ||
| 3510 | qApp->setStyleSheet(ts.readAll()); | ||
| 3511 | setStyleSheet(ts.readAll()); | ||
| 3507 | } else { | 3512 | } else { |
| 3508 | const QString theme_uri(QLatin1Char{':'} + current_theme + QStringLiteral("/style.qss")); | 3513 | LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", |
| 3509 | QFile f(theme_uri); | 3514 | UISettings::values.theme.toStdString()); |
| 3510 | if (f.open(QFile::ReadOnly | QFile::Text)) { | 3515 | qApp->setStyleSheet({}); |
| 3511 | QTextStream ts(&f); | 3516 | setStyleSheet({}); |
| 3512 | qApp->setStyleSheet(ts.readAll()); | ||
| 3513 | setStyleSheet(ts.readAll()); | ||
| 3514 | } else { | ||
| 3515 | LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); | ||
| 3516 | } | ||
| 3517 | QIcon::setThemeName(current_theme); | ||
| 3518 | } | 3517 | } |
| 3519 | 3518 | ||
| 3519 | QIcon::setThemeName(current_theme); | ||
| 3520 | QIcon::setThemeSearchPaths(theme_paths); | 3520 | QIcon::setThemeSearchPaths(theme_paths); |
| 3521 | } | 3521 | } |
| 3522 | 3522 | ||
| @@ -3552,9 +3552,6 @@ void GMainWindow::OnLanguageChanged(const QString& locale) { | |||
| 3552 | LoadTranslation(); | 3552 | LoadTranslation(); |
| 3553 | ui->retranslateUi(this); | 3553 | ui->retranslateUi(this); |
| 3554 | UpdateWindowTitle(); | 3554 | UpdateWindowTitle(); |
| 3555 | |||
| 3556 | if (emulation_running) | ||
| 3557 | ui->action_Start->setText(tr("&Continue")); | ||
| 3558 | } | 3555 | } |
| 3559 | 3556 | ||
| 3560 | void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | 3557 | void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 24633ff2d..0fd41ed4f 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -177,6 +177,7 @@ public slots: | |||
| 177 | void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args, | 177 | void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args, |
| 178 | bool is_local); | 178 | bool is_local); |
| 179 | void OnAppFocusStateChanged(Qt::ApplicationState state); | 179 | void OnAppFocusStateChanged(Qt::ApplicationState state); |
| 180 | void OnTasStateChanged(); | ||
| 180 | 181 | ||
| 181 | private: | 182 | private: |
| 182 | void RegisterMetaTypes(); | 183 | void RegisterMetaTypes(); |
| @@ -190,6 +191,7 @@ private: | |||
| 190 | 191 | ||
| 191 | void ConnectWidgetEvents(); | 192 | void ConnectWidgetEvents(); |
| 192 | void ConnectMenuEvents(); | 193 | void ConnectMenuEvents(); |
| 194 | void UpdateMenuState(); | ||
| 193 | 195 | ||
| 194 | void PreventOSSleep(); | 196 | void PreventOSSleep(); |
| 195 | void AllowOSSleep(); | 197 | void AllowOSSleep(); |
| @@ -239,7 +241,9 @@ private: | |||
| 239 | 241 | ||
| 240 | private slots: | 242 | private slots: |
| 241 | void OnStartGame(); | 243 | void OnStartGame(); |
| 244 | void OnRestartGame(); | ||
| 242 | void OnPauseGame(); | 245 | void OnPauseGame(); |
| 246 | void OnPauseContinueGame(); | ||
| 243 | void OnStopGame(); | 247 | void OnStopGame(); |
| 244 | void OnMenuReportCompatibility(); | 248 | void OnMenuReportCompatibility(); |
| 245 | void OnOpenModsPage(); | 249 | void OnOpenModsPage(); |
| @@ -268,6 +272,9 @@ private slots: | |||
| 268 | void OnMenuRecentFile(); | 272 | void OnMenuRecentFile(); |
| 269 | void OnConfigure(); | 273 | void OnConfigure(); |
| 270 | void OnConfigureTas(); | 274 | void OnConfigureTas(); |
| 275 | void OnTasStartStop(); | ||
| 276 | void OnTasRecord(); | ||
| 277 | void OnTasReset(); | ||
| 271 | void OnConfigurePerGame(); | 278 | void OnConfigurePerGame(); |
| 272 | void OnLoadAmiibo(); | 279 | void OnLoadAmiibo(); |
| 273 | void OnOpenYuzuFolder(); | 280 | void OnOpenYuzuFolder(); |
| @@ -290,6 +297,9 @@ private slots: | |||
| 290 | void OnMouseActivity(); | 297 | void OnMouseActivity(); |
| 291 | 298 | ||
| 292 | private: | 299 | private: |
| 300 | /// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry. | ||
| 301 | void LinkActionShortcut(QAction* action, const QString& action_name); | ||
| 302 | |||
| 293 | void RemoveBaseContent(u64 program_id, const QString& entry_type); | 303 | void RemoveBaseContent(u64 program_id, const QString& entry_type); |
| 294 | void RemoveUpdateContent(u64 program_id, const QString& entry_type); | 304 | void RemoveUpdateContent(u64 program_id, const QString& entry_type); |
| 295 | void RemoveAddOnContent(u64 program_id, const QString& entry_type); | 305 | void RemoveAddOnContent(u64 program_id, const QString& entry_type); |
| @@ -313,6 +323,7 @@ private: | |||
| 313 | void OpenURL(const QUrl& url); | 323 | void OpenURL(const QUrl& url); |
| 314 | void LoadTranslation(); | 324 | void LoadTranslation(); |
| 315 | void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); | 325 | void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); |
| 326 | |||
| 316 | QString GetTasStateDescription() const; | 327 | QString GetTasStateDescription() const; |
| 317 | 328 | ||
| 318 | std::unique_ptr<Ui::MainWindow> ui; | 329 | std::unique_ptr<Ui::MainWindow> ui; |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index a62e39a06..5719b2ee4 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -66,7 +66,6 @@ | |||
| 66 | <property name="title"> | 66 | <property name="title"> |
| 67 | <string>&Emulation</string> | 67 | <string>&Emulation</string> |
| 68 | </property> | 68 | </property> |
| 69 | <addaction name="action_Start"/> | ||
| 70 | <addaction name="action_Pause"/> | 69 | <addaction name="action_Pause"/> |
| 71 | <addaction name="action_Stop"/> | 70 | <addaction name="action_Stop"/> |
| 72 | <addaction name="action_Restart"/> | 71 | <addaction name="action_Restart"/> |
| @@ -79,39 +78,39 @@ | |||
| 79 | <string>&View</string> | 78 | <string>&View</string> |
| 80 | </property> | 79 | </property> |
| 81 | <widget class="QMenu" name="menu_Reset_Window_Size"> | 80 | <widget class="QMenu" name="menu_Reset_Window_Size"> |
| 82 | <property name="title"> | 81 | <property name="title"> |
| 83 | <string>&Reset Window Size</string> | 82 | <string>&Reset Window Size</string> |
| 84 | </property> | 83 | </property> |
| 84 | </widget> | ||
| 85 | <widget class="QMenu" name="menu_View_Debugging"> | ||
| 86 | <property name="title"> | ||
| 87 | <string>&Debugging</string> | ||
| 88 | </property> | ||
| 85 | </widget> | 89 | </widget> |
| 86 | <action name="action_Reset_Window_Size_720"> | 90 | <action name="action_Reset_Window_Size_720"> |
| 87 | <property name="text"> | 91 | <property name="text"> |
| 88 | <string>Reset Window Size to &720p</string> | 92 | <string>Reset Window Size to &720p</string> |
| 89 | </property> | 93 | </property> |
| 90 | <property name="iconText"> | 94 | <property name="iconText"> |
| 91 | <string>Reset Window Size to 720p</string> | 95 | <string>Reset Window Size to 720p</string> |
| 92 | </property> | 96 | </property> |
| 93 | </action> | 97 | </action> |
| 94 | <action name="action_Reset_Window_Size_900"> | 98 | <action name="action_Reset_Window_Size_900"> |
| 95 | <property name="text"> | 99 | <property name="text"> |
| 96 | <string>Reset Window Size to &900p</string> | 100 | <string>Reset Window Size to &900p</string> |
| 97 | </property> | 101 | </property> |
| 98 | <property name="iconText"> | 102 | <property name="iconText"> |
| 99 | <string>Reset Window Size to 900p</string> | 103 | <string>Reset Window Size to 900p</string> |
| 100 | </property> | 104 | </property> |
| 101 | </action> | 105 | </action> |
| 102 | <action name="action_Reset_Window_Size_1080"> | 106 | <action name="action_Reset_Window_Size_1080"> |
| 103 | <property name="text"> | 107 | <property name="text"> |
| 104 | <string>Reset Window Size to &1080p</string> | 108 | <string>Reset Window Size to &1080p</string> |
| 105 | </property> | ||
| 106 | <property name="iconText"> | ||
| 107 | <string>Reset Window Size to 1080p</string> | ||
| 108 | </property> | ||
| 109 | </action> | ||
| 110 | <widget class="QMenu" name="menu_View_Debugging"> | ||
| 111 | <property name="title"> | ||
| 112 | <string>&Debugging</string> | ||
| 113 | </property> | 109 | </property> |
| 114 | </widget> | 110 | <property name="iconText"> |
| 111 | <string>Reset Window Size to 1080p</string> | ||
| 112 | </property> | ||
| 113 | </action> | ||
| 115 | <addaction name="action_Fullscreen"/> | 114 | <addaction name="action_Fullscreen"/> |
| 116 | <addaction name="action_Single_Window_Mode"/> | 115 | <addaction name="action_Single_Window_Mode"/> |
| 117 | <addaction name="action_Display_Dock_Widget_Headers"/> | 116 | <addaction name="action_Display_Dock_Widget_Headers"/> |
| @@ -125,10 +124,20 @@ | |||
| 125 | <property name="title"> | 124 | <property name="title"> |
| 126 | <string>&Tools</string> | 125 | <string>&Tools</string> |
| 127 | </property> | 126 | </property> |
| 127 | <widget class="QMenu" name="menuTAS"> | ||
| 128 | <property name="title"> | ||
| 129 | <string>&TAS</string> | ||
| 130 | </property> | ||
| 131 | <addaction name="action_TAS_Start"/> | ||
| 132 | <addaction name="action_TAS_Record"/> | ||
| 133 | <addaction name="action_TAS_Reset"/> | ||
| 134 | <addaction name="separator"/> | ||
| 135 | <addaction name="action_Configure_Tas"/> | ||
| 136 | </widget> | ||
| 128 | <addaction name="action_Rederive"/> | 137 | <addaction name="action_Rederive"/> |
| 129 | <addaction name="separator"/> | 138 | <addaction name="separator"/> |
| 130 | <addaction name="action_Capture_Screenshot"/> | 139 | <addaction name="action_Capture_Screenshot"/> |
| 131 | <addaction name="action_Configure_Tas"/> | 140 | <addaction name="menuTAS"/> |
| 132 | </widget> | 141 | </widget> |
| 133 | <widget class="QMenu" name="menu_Help"> | 142 | <widget class="QMenu" name="menu_Help"> |
| 134 | <property name="title"> | 143 | <property name="title"> |
| @@ -170,14 +179,6 @@ | |||
| 170 | <string>E&xit</string> | 179 | <string>E&xit</string> |
| 171 | </property> | 180 | </property> |
| 172 | </action> | 181 | </action> |
| 173 | <action name="action_Start"> | ||
| 174 | <property name="enabled"> | ||
| 175 | <bool>false</bool> | ||
| 176 | </property> | ||
| 177 | <property name="text"> | ||
| 178 | <string>&Start</string> | ||
| 179 | </property> | ||
| 180 | </action> | ||
| 181 | <action name="action_Pause"> | 182 | <action name="action_Pause"> |
| 182 | <property name="enabled"> | 183 | <property name="enabled"> |
| 183 | <bool>false</bool> | 184 | <bool>false</bool> |
| @@ -309,7 +310,7 @@ | |||
| 309 | </action> | 310 | </action> |
| 310 | <action name="action_Configure_Tas"> | 311 | <action name="action_Configure_Tas"> |
| 311 | <property name="text"> | 312 | <property name="text"> |
| 312 | <string>Configure &TAS...</string> | 313 | <string>&Configure TAS...</string> |
| 313 | </property> | 314 | </property> |
| 314 | </action> | 315 | </action> |
| 315 | <action name="action_Configure_Current_Game"> | 316 | <action name="action_Configure_Current_Game"> |
| @@ -320,6 +321,30 @@ | |||
| 320 | <string>Configure C&urrent Game...</string> | 321 | <string>Configure C&urrent Game...</string> |
| 321 | </property> | 322 | </property> |
| 322 | </action> | 323 | </action> |
| 324 | <action name="action_TAS_Start"> | ||
| 325 | <property name="enabled"> | ||
| 326 | <bool>false</bool> | ||
| 327 | </property> | ||
| 328 | <property name="text"> | ||
| 329 | <string>&Start</string> | ||
| 330 | </property> | ||
| 331 | </action> | ||
| 332 | <action name="action_TAS_Reset"> | ||
| 333 | <property name="enabled"> | ||
| 334 | <bool>false</bool> | ||
| 335 | </property> | ||
| 336 | <property name="text"> | ||
| 337 | <string>&Reset</string> | ||
| 338 | </property> | ||
| 339 | </action> | ||
| 340 | <action name="action_TAS_Record"> | ||
| 341 | <property name="enabled"> | ||
| 342 | <bool>false</bool> | ||
| 343 | </property> | ||
| 344 | <property name="text"> | ||
| 345 | <string>R&ecord</string> | ||
| 346 | </property> | ||
| 347 | </action> | ||
| 323 | </widget> | 348 | </widget> |
| 324 | <resources> | 349 | <resources> |
| 325 | <include location="yuzu.qrc"/> | 350 | <include location="yuzu.qrc"/> |
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp index 37499fc85..21683576c 100644 --- a/src/yuzu/uisettings.cpp +++ b/src/yuzu/uisettings.cpp | |||
| @@ -7,8 +7,8 @@ | |||
| 7 | namespace UISettings { | 7 | namespace UISettings { |
| 8 | 8 | ||
| 9 | const Themes themes{{ | 9 | const Themes themes{{ |
| 10 | {"Light", "default"}, | 10 | {"Default", "default"}, |
| 11 | {"Light Colorful", "colorful"}, | 11 | {"Default Colorful", "colorful"}, |
| 12 | {"Dark", "qdarkstyle"}, | 12 | {"Dark", "qdarkstyle"}, |
| 13 | {"Dark Colorful", "colorful_dark"}, | 13 | {"Dark Colorful", "colorful_dark"}, |
| 14 | {"Midnight Blue", "qdarkstyle_midnight_blue"}, | 14 | {"Midnight Blue", "qdarkstyle_midnight_blue"}, |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 936914ef3..a610e7e25 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -74,7 +74,6 @@ struct Values { | |||
| 74 | QString game_dir_deprecated; | 74 | QString game_dir_deprecated; |
| 75 | bool game_dir_deprecated_deepscan; | 75 | bool game_dir_deprecated_deepscan; |
| 76 | QVector<UISettings::GameDir> game_dirs; | 76 | QVector<UISettings::GameDir> game_dirs; |
| 77 | QVector<u64> favorited_ids; | ||
| 78 | QStringList recent_files; | 77 | QStringList recent_files; |
| 79 | QString language; | 78 | QString language; |
| 80 | 79 | ||
| @@ -96,6 +95,8 @@ struct Values { | |||
| 96 | Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; | 95 | Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; |
| 97 | std::atomic_bool is_game_list_reload_pending{false}; | 96 | std::atomic_bool is_game_list_reload_pending{false}; |
| 98 | Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; | 97 | Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; |
| 98 | Settings::BasicSetting<bool> favorites_expanded{true, "favorites_expanded"}; | ||
| 99 | QVector<u64> favorited_ids; | ||
| 99 | 100 | ||
| 100 | bool configuration_applied; | 101 | bool configuration_applied; |
| 101 | bool reset_to_defaults; | 102 | bool reset_to_defaults; |
diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp new file mode 100644 index 000000000..86fb28b9f --- /dev/null +++ b/src/yuzu/util/controller_navigation.cpp | |||
| @@ -0,0 +1,177 @@ | |||
| 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_input.h" | ||
| 6 | #include "core/hid/emulated_controller.h" | ||
| 7 | #include "core/hid/hid_core.h" | ||
| 8 | #include "yuzu/util/controller_navigation.h" | ||
| 9 | |||
| 10 | ControllerNavigation::ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent) { | ||
| 11 | player1_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 12 | handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 13 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 14 | .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); }, | ||
| 15 | .is_npad_service = false, | ||
| 16 | }; | ||
| 17 | player1_callback_key = player1_controller->SetCallback(engine_callback); | ||
| 18 | handheld_callback_key = handheld_controller->SetCallback(engine_callback); | ||
| 19 | is_controller_set = true; | ||
| 20 | } | ||
| 21 | |||
| 22 | ControllerNavigation::~ControllerNavigation() { | ||
| 23 | UnloadController(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void ControllerNavigation::UnloadController() { | ||
| 27 | if (is_controller_set) { | ||
| 28 | player1_controller->DeleteCallback(player1_callback_key); | ||
| 29 | handheld_controller->DeleteCallback(handheld_callback_key); | ||
| 30 | is_controller_set = false; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_button, | ||
| 35 | Qt::Key key) { | ||
| 36 | if (button_values[native_button].value && !button_values[native_button].locked) { | ||
| 37 | emit TriggerKeyboardEvent(key); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) { | ||
| 42 | std::lock_guard lock{mutex}; | ||
| 43 | if (type == Core::HID::ControllerTriggerType::Button) { | ||
| 44 | ControllerUpdateButton(); | ||
| 45 | return; | ||
| 46 | } | ||
| 47 | |||
| 48 | if (type == Core::HID::ControllerTriggerType::Stick) { | ||
| 49 | ControllerUpdateStick(); | ||
| 50 | return; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | void ControllerNavigation::ControllerUpdateButton() { | ||
| 55 | const auto controller_type = player1_controller->GetNpadStyleIndex(); | ||
| 56 | const auto& player1_buttons = player1_controller->GetButtonsValues(); | ||
| 57 | const auto& handheld_buttons = handheld_controller->GetButtonsValues(); | ||
| 58 | |||
| 59 | for (std::size_t i = 0; i < player1_buttons.size(); ++i) { | ||
| 60 | const bool button = player1_buttons[i].value || handheld_buttons[i].value; | ||
| 61 | // Trigger only once | ||
| 62 | button_values[i].locked = button == button_values[i].value; | ||
| 63 | button_values[i].value = button; | ||
| 64 | } | ||
| 65 | |||
| 66 | switch (controller_type) { | ||
| 67 | case Core::HID::NpadStyleIndex::ProController: | ||
| 68 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 69 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 70 | case Core::HID::NpadStyleIndex::GameCube: | ||
| 71 | TriggerButton(Settings::NativeButton::A, Qt::Key_Enter); | ||
| 72 | TriggerButton(Settings::NativeButton::B, Qt::Key_Escape); | ||
| 73 | TriggerButton(Settings::NativeButton::DDown, Qt::Key_Down); | ||
| 74 | TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Left); | ||
| 75 | TriggerButton(Settings::NativeButton::DRight, Qt::Key_Right); | ||
| 76 | TriggerButton(Settings::NativeButton::DUp, Qt::Key_Up); | ||
| 77 | break; | ||
| 78 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 79 | TriggerButton(Settings::NativeButton::DDown, Qt::Key_Enter); | ||
| 80 | TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Escape); | ||
| 81 | break; | ||
| 82 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 83 | TriggerButton(Settings::NativeButton::X, Qt::Key_Enter); | ||
| 84 | TriggerButton(Settings::NativeButton::A, Qt::Key_Escape); | ||
| 85 | break; | ||
| 86 | default: | ||
| 87 | break; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | void ControllerNavigation::ControllerUpdateStick() { | ||
| 92 | const auto controller_type = player1_controller->GetNpadStyleIndex(); | ||
| 93 | const auto& player1_sticks = player1_controller->GetSticksValues(); | ||
| 94 | const auto& handheld_sticks = player1_controller->GetSticksValues(); | ||
| 95 | bool update = false; | ||
| 96 | |||
| 97 | for (std::size_t i = 0; i < player1_sticks.size(); ++i) { | ||
| 98 | const Common::Input::StickStatus stick{ | ||
| 99 | .left = player1_sticks[i].left || handheld_sticks[i].left, | ||
| 100 | .right = player1_sticks[i].right || handheld_sticks[i].right, | ||
| 101 | .up = player1_sticks[i].up || handheld_sticks[i].up, | ||
| 102 | .down = player1_sticks[i].down || handheld_sticks[i].down, | ||
| 103 | }; | ||
| 104 | // Trigger only once | ||
| 105 | if (stick.down != stick_values[i].down || stick.left != stick_values[i].left || | ||
| 106 | stick.right != stick_values[i].right || stick.up != stick_values[i].up) { | ||
| 107 | update = true; | ||
| 108 | } | ||
| 109 | stick_values[i] = stick; | ||
| 110 | } | ||
| 111 | |||
| 112 | if (!update) { | ||
| 113 | return; | ||
| 114 | } | ||
| 115 | |||
| 116 | switch (controller_type) { | ||
| 117 | case Core::HID::NpadStyleIndex::ProController: | ||
| 118 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 119 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 120 | case Core::HID::NpadStyleIndex::GameCube: | ||
| 121 | if (stick_values[Settings::NativeAnalog::LStick].down) { | ||
| 122 | emit TriggerKeyboardEvent(Qt::Key_Down); | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | if (stick_values[Settings::NativeAnalog::LStick].left) { | ||
| 126 | emit TriggerKeyboardEvent(Qt::Key_Left); | ||
| 127 | return; | ||
| 128 | } | ||
| 129 | if (stick_values[Settings::NativeAnalog::LStick].right) { | ||
| 130 | emit TriggerKeyboardEvent(Qt::Key_Right); | ||
| 131 | return; | ||
| 132 | } | ||
| 133 | if (stick_values[Settings::NativeAnalog::LStick].up) { | ||
| 134 | emit TriggerKeyboardEvent(Qt::Key_Up); | ||
| 135 | return; | ||
| 136 | } | ||
| 137 | break; | ||
| 138 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 139 | if (stick_values[Settings::NativeAnalog::LStick].left) { | ||
| 140 | emit TriggerKeyboardEvent(Qt::Key_Down); | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | if (stick_values[Settings::NativeAnalog::LStick].up) { | ||
| 144 | emit TriggerKeyboardEvent(Qt::Key_Left); | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | if (stick_values[Settings::NativeAnalog::LStick].down) { | ||
| 148 | emit TriggerKeyboardEvent(Qt::Key_Right); | ||
| 149 | return; | ||
| 150 | } | ||
| 151 | if (stick_values[Settings::NativeAnalog::LStick].right) { | ||
| 152 | emit TriggerKeyboardEvent(Qt::Key_Up); | ||
| 153 | return; | ||
| 154 | } | ||
| 155 | break; | ||
| 156 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 157 | if (stick_values[Settings::NativeAnalog::RStick].right) { | ||
| 158 | emit TriggerKeyboardEvent(Qt::Key_Down); | ||
| 159 | return; | ||
| 160 | } | ||
| 161 | if (stick_values[Settings::NativeAnalog::RStick].down) { | ||
| 162 | emit TriggerKeyboardEvent(Qt::Key_Left); | ||
| 163 | return; | ||
| 164 | } | ||
| 165 | if (stick_values[Settings::NativeAnalog::RStick].up) { | ||
| 166 | emit TriggerKeyboardEvent(Qt::Key_Right); | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | if (stick_values[Settings::NativeAnalog::RStick].left) { | ||
| 170 | emit TriggerKeyboardEvent(Qt::Key_Up); | ||
| 171 | return; | ||
| 172 | } | ||
| 173 | break; | ||
| 174 | default: | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | } | ||
diff --git a/src/yuzu/util/controller_navigation.h b/src/yuzu/util/controller_navigation.h new file mode 100644 index 000000000..7c616a088 --- /dev/null +++ b/src/yuzu/util/controller_navigation.h | |||
| @@ -0,0 +1,51 @@ | |||
| 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 <QKeyEvent> | ||
| 8 | #include <QObject> | ||
| 9 | |||
| 10 | #include "common/input.h" | ||
| 11 | #include "common/settings_input.h" | ||
| 12 | |||
| 13 | namespace Core::HID { | ||
| 14 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; | ||
| 15 | using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; | ||
| 16 | enum class ControllerTriggerType; | ||
| 17 | class EmulatedController; | ||
| 18 | class HIDCore; | ||
| 19 | } // namespace Core::HID | ||
| 20 | |||
| 21 | class ControllerNavigation : public QObject { | ||
| 22 | Q_OBJECT | ||
| 23 | |||
| 24 | public: | ||
| 25 | explicit ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent = nullptr); | ||
| 26 | ~ControllerNavigation(); | ||
| 27 | |||
| 28 | /// Disables events from the emulated controller | ||
| 29 | void UnloadController(); | ||
| 30 | |||
| 31 | signals: | ||
| 32 | void TriggerKeyboardEvent(Qt::Key key); | ||
| 33 | |||
| 34 | private: | ||
| 35 | void TriggerButton(Settings::NativeButton::Values native_button, Qt::Key key); | ||
| 36 | void ControllerUpdateEvent(Core::HID::ControllerTriggerType type); | ||
| 37 | |||
| 38 | void ControllerUpdateButton(); | ||
| 39 | |||
| 40 | void ControllerUpdateStick(); | ||
| 41 | |||
| 42 | Core::HID::ButtonValues button_values{}; | ||
| 43 | Core::HID::SticksValues stick_values{}; | ||
| 44 | |||
| 45 | int player1_callback_key{}; | ||
| 46 | int handheld_callback_key{}; | ||
| 47 | bool is_controller_set{}; | ||
| 48 | mutable std::mutex mutex; | ||
| 49 | Core::HID::EmulatedController* player1_controller; | ||
| 50 | Core::HID::EmulatedController* handheld_controller; | ||
| 51 | }; | ||
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp index 95b148545..c66dfbdff 100644 --- a/src/yuzu/util/overlay_dialog.cpp +++ b/src/yuzu/util/overlay_dialog.cpp | |||
| @@ -6,7 +6,8 @@ | |||
| 6 | #include <QScreen> | 6 | #include <QScreen> |
| 7 | 7 | ||
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/frontend/input_interpreter.h" | 9 | #include "core/hid/hid_types.h" |
| 10 | #include "core/hid/input_interpreter.h" | ||
| 10 | #include "ui_overlay_dialog.h" | 11 | #include "ui_overlay_dialog.h" |
| 11 | #include "yuzu/util/overlay_dialog.h" | 12 | #include "yuzu/util/overlay_dialog.h" |
| 12 | 13 | ||
| @@ -179,9 +180,9 @@ void OverlayDialog::MoveAndResizeWindow() { | |||
| 179 | QDialog::resize(width, height); | 180 | QDialog::resize(width, height); |
| 180 | } | 181 | } |
| 181 | 182 | ||
| 182 | template <HIDButton... T> | 183 | template <Core::HID::NpadButton... T> |
| 183 | void OverlayDialog::HandleButtonPressedOnce() { | 184 | void OverlayDialog::HandleButtonPressedOnce() { |
| 184 | const auto f = [this](HIDButton button) { | 185 | const auto f = [this](Core::HID::NpadButton button) { |
| 185 | if (input_interpreter->IsButtonPressedOnce(button)) { | 186 | if (input_interpreter->IsButtonPressedOnce(button)) { |
| 186 | TranslateButtonPress(button); | 187 | TranslateButtonPress(button); |
| 187 | } | 188 | } |
| @@ -190,7 +191,7 @@ void OverlayDialog::HandleButtonPressedOnce() { | |||
| 190 | (f(T), ...); | 191 | (f(T), ...); |
| 191 | } | 192 | } |
| 192 | 193 | ||
| 193 | void OverlayDialog::TranslateButtonPress(HIDButton button) { | 194 | void OverlayDialog::TranslateButtonPress(Core::HID::NpadButton button) { |
| 194 | QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel; | 195 | QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel; |
| 195 | QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label; | 196 | QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label; |
| 196 | 197 | ||
| @@ -198,20 +199,20 @@ void OverlayDialog::TranslateButtonPress(HIDButton button) { | |||
| 198 | // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it | 199 | // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it |
| 199 | 200 | ||
| 200 | switch (button) { | 201 | switch (button) { |
| 201 | case HIDButton::A: | 202 | case Core::HID::NpadButton::A: |
| 202 | case HIDButton::B: | 203 | case Core::HID::NpadButton::B: |
| 203 | if (left_button->hasFocus()) { | 204 | if (left_button->hasFocus()) { |
| 204 | left_button->click(); | 205 | left_button->click(); |
| 205 | } else if (right_button->hasFocus()) { | 206 | } else if (right_button->hasFocus()) { |
| 206 | right_button->click(); | 207 | right_button->click(); |
| 207 | } | 208 | } |
| 208 | break; | 209 | break; |
| 209 | case HIDButton::DLeft: | 210 | case Core::HID::NpadButton::Left: |
| 210 | case HIDButton::LStickLeft: | 211 | case Core::HID::NpadButton::StickLLeft: |
| 211 | focusPreviousChild(); | 212 | focusPreviousChild(); |
| 212 | break; | 213 | break; |
| 213 | case HIDButton::DRight: | 214 | case Core::HID::NpadButton::Right: |
| 214 | case HIDButton::LStickRight: | 215 | case Core::HID::NpadButton::StickLRight: |
| 215 | focusNextChild(); | 216 | focusNextChild(); |
| 216 | break; | 217 | break; |
| 217 | default: | 218 | default: |
| @@ -241,8 +242,10 @@ void OverlayDialog::InputThread() { | |||
| 241 | while (input_thread_running) { | 242 | while (input_thread_running) { |
| 242 | input_interpreter->PollInput(); | 243 | input_interpreter->PollInput(); |
| 243 | 244 | ||
| 244 | HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::DLeft, HIDButton::DRight, | 245 | HandleButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B, |
| 245 | HIDButton::LStickLeft, HIDButton::LStickRight>(); | 246 | Core::HID::NpadButton::Left, Core::HID::NpadButton::Right, |
| 247 | Core::HID::NpadButton::StickLLeft, | ||
| 248 | Core::HID::NpadButton::StickLRight>(); | ||
| 246 | 249 | ||
| 247 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); | 250 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| 248 | } | 251 | } |
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h index e8c388bd0..d8a140ff3 100644 --- a/src/yuzu/util/overlay_dialog.h +++ b/src/yuzu/util/overlay_dialog.h | |||
| @@ -13,14 +13,16 @@ | |||
| 13 | 13 | ||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | 15 | ||
| 16 | enum class HIDButton : u8; | ||
| 17 | |||
| 18 | class InputInterpreter; | 16 | class InputInterpreter; |
| 19 | 17 | ||
| 20 | namespace Core { | 18 | namespace Core { |
| 21 | class System; | 19 | class System; |
| 22 | } | 20 | } |
| 23 | 21 | ||
| 22 | namespace Core::HID { | ||
| 23 | enum class NpadButton : u64; | ||
| 24 | } | ||
| 25 | |||
| 24 | namespace Ui { | 26 | namespace Ui { |
| 25 | class OverlayDialog; | 27 | class OverlayDialog; |
| 26 | } | 28 | } |
| @@ -79,7 +81,7 @@ private: | |||
| 79 | * | 81 | * |
| 80 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. | 82 | * @tparam HIDButton The list of buttons that can be converted into keyboard input. |
| 81 | */ | 83 | */ |
| 82 | template <HIDButton... T> | 84 | template <Core::HID::NpadButton... T> |
| 83 | void HandleButtonPressedOnce(); | 85 | void HandleButtonPressedOnce(); |
| 84 | 86 | ||
| 85 | /** | 87 | /** |
| @@ -87,7 +89,7 @@ private: | |||
| 87 | * | 89 | * |
| 88 | * @param button The button press to process. | 90 | * @param button The button press to process. |
| 89 | */ | 91 | */ |
| 90 | void TranslateButtonPress(HIDButton button); | 92 | void TranslateButtonPress(Core::HID::NpadButton button); |
| 91 | 93 | ||
| 92 | void StartInputThread(); | 94 | void StartInputThread(); |
| 93 | void StopInputThread(); | 95 | void StopInputThread(); |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 33241ea98..8e9c7d211 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -24,7 +24,6 @@ | |||
| 24 | #include "common/settings.h" | 24 | #include "common/settings.h" |
| 25 | #include "core/hle/service/acc/profile_manager.h" | 25 | #include "core/hle/service/acc/profile_manager.h" |
| 26 | #include "input_common/main.h" | 26 | #include "input_common/main.h" |
| 27 | #include "input_common/udp/client.h" | ||
| 28 | #include "yuzu_cmd/config.h" | 27 | #include "yuzu_cmd/config.h" |
| 29 | #include "yuzu_cmd/default_ini.h" | 28 | #include "yuzu_cmd/default_ini.h" |
| 30 | 29 | ||
| @@ -84,163 +83,6 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> | |||
| 84 | }, | 83 | }, |
| 85 | }}; | 84 | }}; |
| 86 | 85 | ||
| 87 | static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = { | ||
| 88 | SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE, | ||
| 89 | SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, | ||
| 90 | }; | ||
| 91 | |||
| 92 | static const std::array<int, 0x8A> keyboard_keys = { | ||
| 93 | 0, | ||
| 94 | 0, | ||
| 95 | 0, | ||
| 96 | 0, | ||
| 97 | SDL_SCANCODE_A, | ||
| 98 | SDL_SCANCODE_B, | ||
| 99 | SDL_SCANCODE_C, | ||
| 100 | SDL_SCANCODE_D, | ||
| 101 | SDL_SCANCODE_E, | ||
| 102 | SDL_SCANCODE_F, | ||
| 103 | SDL_SCANCODE_G, | ||
| 104 | SDL_SCANCODE_H, | ||
| 105 | SDL_SCANCODE_I, | ||
| 106 | SDL_SCANCODE_J, | ||
| 107 | SDL_SCANCODE_K, | ||
| 108 | SDL_SCANCODE_L, | ||
| 109 | SDL_SCANCODE_M, | ||
| 110 | SDL_SCANCODE_N, | ||
| 111 | SDL_SCANCODE_O, | ||
| 112 | SDL_SCANCODE_P, | ||
| 113 | SDL_SCANCODE_Q, | ||
| 114 | SDL_SCANCODE_R, | ||
| 115 | SDL_SCANCODE_S, | ||
| 116 | SDL_SCANCODE_T, | ||
| 117 | SDL_SCANCODE_U, | ||
| 118 | SDL_SCANCODE_V, | ||
| 119 | SDL_SCANCODE_W, | ||
| 120 | SDL_SCANCODE_X, | ||
| 121 | SDL_SCANCODE_Y, | ||
| 122 | SDL_SCANCODE_Z, | ||
| 123 | SDL_SCANCODE_1, | ||
| 124 | SDL_SCANCODE_2, | ||
| 125 | SDL_SCANCODE_3, | ||
| 126 | SDL_SCANCODE_4, | ||
| 127 | SDL_SCANCODE_5, | ||
| 128 | SDL_SCANCODE_6, | ||
| 129 | SDL_SCANCODE_7, | ||
| 130 | SDL_SCANCODE_8, | ||
| 131 | SDL_SCANCODE_9, | ||
| 132 | SDL_SCANCODE_0, | ||
| 133 | SDL_SCANCODE_RETURN, | ||
| 134 | SDL_SCANCODE_ESCAPE, | ||
| 135 | SDL_SCANCODE_BACKSPACE, | ||
| 136 | SDL_SCANCODE_TAB, | ||
| 137 | SDL_SCANCODE_SPACE, | ||
| 138 | SDL_SCANCODE_MINUS, | ||
| 139 | SDL_SCANCODE_EQUALS, | ||
| 140 | SDL_SCANCODE_LEFTBRACKET, | ||
| 141 | SDL_SCANCODE_RIGHTBRACKET, | ||
| 142 | SDL_SCANCODE_BACKSLASH, | ||
| 143 | 0, | ||
| 144 | SDL_SCANCODE_SEMICOLON, | ||
| 145 | SDL_SCANCODE_APOSTROPHE, | ||
| 146 | SDL_SCANCODE_GRAVE, | ||
| 147 | SDL_SCANCODE_COMMA, | ||
| 148 | SDL_SCANCODE_PERIOD, | ||
| 149 | SDL_SCANCODE_SLASH, | ||
| 150 | SDL_SCANCODE_CAPSLOCK, | ||
| 151 | |||
| 152 | SDL_SCANCODE_F1, | ||
| 153 | SDL_SCANCODE_F2, | ||
| 154 | SDL_SCANCODE_F3, | ||
| 155 | SDL_SCANCODE_F4, | ||
| 156 | SDL_SCANCODE_F5, | ||
| 157 | SDL_SCANCODE_F6, | ||
| 158 | SDL_SCANCODE_F7, | ||
| 159 | SDL_SCANCODE_F8, | ||
| 160 | SDL_SCANCODE_F9, | ||
| 161 | SDL_SCANCODE_F10, | ||
| 162 | SDL_SCANCODE_F11, | ||
| 163 | SDL_SCANCODE_F12, | ||
| 164 | |||
| 165 | 0, | ||
| 166 | SDL_SCANCODE_SCROLLLOCK, | ||
| 167 | SDL_SCANCODE_PAUSE, | ||
| 168 | SDL_SCANCODE_INSERT, | ||
| 169 | SDL_SCANCODE_HOME, | ||
| 170 | SDL_SCANCODE_PAGEUP, | ||
| 171 | SDL_SCANCODE_DELETE, | ||
| 172 | SDL_SCANCODE_END, | ||
| 173 | SDL_SCANCODE_PAGEDOWN, | ||
| 174 | SDL_SCANCODE_RIGHT, | ||
| 175 | SDL_SCANCODE_LEFT, | ||
| 176 | SDL_SCANCODE_DOWN, | ||
| 177 | SDL_SCANCODE_UP, | ||
| 178 | |||
| 179 | SDL_SCANCODE_NUMLOCKCLEAR, | ||
| 180 | SDL_SCANCODE_KP_DIVIDE, | ||
| 181 | SDL_SCANCODE_KP_MULTIPLY, | ||
| 182 | SDL_SCANCODE_KP_MINUS, | ||
| 183 | SDL_SCANCODE_KP_PLUS, | ||
| 184 | SDL_SCANCODE_KP_ENTER, | ||
| 185 | SDL_SCANCODE_KP_1, | ||
| 186 | SDL_SCANCODE_KP_2, | ||
| 187 | SDL_SCANCODE_KP_3, | ||
| 188 | SDL_SCANCODE_KP_4, | ||
| 189 | SDL_SCANCODE_KP_5, | ||
| 190 | SDL_SCANCODE_KP_6, | ||
| 191 | SDL_SCANCODE_KP_7, | ||
| 192 | SDL_SCANCODE_KP_8, | ||
| 193 | SDL_SCANCODE_KP_9, | ||
| 194 | SDL_SCANCODE_KP_0, | ||
| 195 | SDL_SCANCODE_KP_PERIOD, | ||
| 196 | |||
| 197 | 0, | ||
| 198 | 0, | ||
| 199 | SDL_SCANCODE_POWER, | ||
| 200 | SDL_SCANCODE_KP_EQUALS, | ||
| 201 | |||
| 202 | SDL_SCANCODE_F13, | ||
| 203 | SDL_SCANCODE_F14, | ||
| 204 | SDL_SCANCODE_F15, | ||
| 205 | SDL_SCANCODE_F16, | ||
| 206 | SDL_SCANCODE_F17, | ||
| 207 | SDL_SCANCODE_F18, | ||
| 208 | SDL_SCANCODE_F19, | ||
| 209 | SDL_SCANCODE_F20, | ||
| 210 | SDL_SCANCODE_F21, | ||
| 211 | SDL_SCANCODE_F22, | ||
| 212 | SDL_SCANCODE_F23, | ||
| 213 | SDL_SCANCODE_F24, | ||
| 214 | |||
| 215 | 0, | ||
| 216 | SDL_SCANCODE_HELP, | ||
| 217 | SDL_SCANCODE_MENU, | ||
| 218 | 0, | ||
| 219 | 0, | ||
| 220 | 0, | ||
| 221 | 0, | ||
| 222 | 0, | ||
| 223 | 0, | ||
| 224 | 0, | ||
| 225 | 0, | ||
| 226 | 0, | ||
| 227 | 0, | ||
| 228 | 0, | ||
| 229 | 0, | ||
| 230 | SDL_SCANCODE_KP_COMMA, | ||
| 231 | SDL_SCANCODE_KP_LEFTPAREN, | ||
| 232 | SDL_SCANCODE_KP_RIGHTPAREN, | ||
| 233 | 0, | ||
| 234 | 0, | ||
| 235 | 0, | ||
| 236 | 0, | ||
| 237 | }; | ||
| 238 | |||
| 239 | static const std::array<int, 8> keyboard_mods{ | ||
| 240 | SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI, | ||
| 241 | SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI, | ||
| 242 | }; | ||
| 243 | |||
| 244 | template <> | 86 | template <> |
| 245 | void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { | 87 | void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { |
| 246 | setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); | 88 | setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); |
| @@ -284,16 +126,6 @@ void Config::ReadValues() { | |||
| 284 | } | 126 | } |
| 285 | 127 | ||
| 286 | ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); | 128 | ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); |
| 287 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 288 | std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); | ||
| 289 | Settings::values.mouse_buttons[i] = sdl2_config->Get( | ||
| 290 | "ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i], | ||
| 291 | default_param); | ||
| 292 | if (Settings::values.mouse_buttons[i].empty()) | ||
| 293 | Settings::values.mouse_buttons[i] = default_param; | ||
| 294 | } | ||
| 295 | |||
| 296 | ReadSetting("ControlsGeneral", Settings::values.motion_device); | ||
| 297 | 129 | ||
| 298 | ReadSetting("ControlsGeneral", Settings::values.touch_device); | 130 | ReadSetting("ControlsGeneral", Settings::values.touch_device); |
| 299 | 131 | ||
| @@ -363,21 +195,11 @@ void Config::ReadValues() { | |||
| 363 | Settings::TouchFromButtonMap{"default", {}}); | 195 | Settings::TouchFromButtonMap{"default", {}}); |
| 364 | num_touch_from_button_maps = 1; | 196 | num_touch_from_button_maps = 1; |
| 365 | } | 197 | } |
| 366 | ReadSetting("ControlsGeneral", Settings::values.use_touch_from_button); | ||
| 367 | Settings::values.touch_from_button_map_index = std::clamp( | 198 | Settings::values.touch_from_button_map_index = std::clamp( |
| 368 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); | 199 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); |
| 369 | 200 | ||
| 370 | ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); | 201 | ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); |
| 371 | 202 | ||
| 372 | std::transform(keyboard_keys.begin(), keyboard_keys.end(), | ||
| 373 | Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); | ||
| 374 | std::transform(keyboard_mods.begin(), keyboard_mods.end(), | ||
| 375 | Settings::values.keyboard_keys.begin() + | ||
| 376 | Settings::NativeKeyboard::LeftControlKey, | ||
| 377 | InputCommon::GenerateKeyboardParam); | ||
| 378 | std::transform(keyboard_mods.begin(), keyboard_mods.end(), | ||
| 379 | Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam); | ||
| 380 | |||
| 381 | // Data Storage | 203 | // Data Storage |
| 382 | ReadSetting("Data Storage", Settings::values.use_virtual_sd); | 204 | ReadSetting("Data Storage", Settings::values.use_virtual_sd); |
| 383 | FS::SetYuzuPath(FS::YuzuPath::NANDDir, | 205 | FS::SetYuzuPath(FS::YuzuPath::NANDDir, |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index ecdc271a8..6d613bf7a 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -84,23 +84,10 @@ enable_accurate_vibrations= | |||
| 84 | # 0: Disabled, 1 (default): Enabled | 84 | # 0: Disabled, 1 (default): Enabled |
| 85 | motion_enabled = | 85 | motion_enabled = |
| 86 | 86 | ||
| 87 | # for motion input, the following devices are available: | 87 | # Defines the udp device's touch screen coordinate system for cemuhookudp devices |
| 88 | # - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: | 88 | # - "min_x", "min_y", "max_x", "max_y" |
| 89 | # - "update_period": update period in milliseconds (default to 100) | ||
| 90 | # - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01) | ||
| 91 | # - "cemuhookudp" reads motion input from a udp server that uses cemuhook's udp protocol | ||
| 92 | motion_device= | ||
| 93 | |||
| 94 | # for touch input, the following devices are available: | ||
| 95 | # - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required | ||
| 96 | # - "cemuhookudp" reads touch input from a udp server that uses cemuhook's udp protocol | ||
| 97 | # - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system | ||
| 98 | touch_device= | 89 | touch_device= |
| 99 | 90 | ||
| 100 | # Whether to enable or disable touch input from button | ||
| 101 | # 0 (default): Disabled, 1: Enabled | ||
| 102 | use_touch_from_button= | ||
| 103 | |||
| 104 | # for mapping buttons to touch inputs. | 91 | # for mapping buttons to touch inputs. |
| 105 | #touch_from_button_map=1 | 92 | #touch_from_button_map=1 |
| 106 | #touch_from_button_maps_0_name=default | 93 | #touch_from_button_maps_0_name=default |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 87fce0c23..57f807826 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -9,10 +9,10 @@ | |||
| 9 | #include "common/settings.h" | 9 | #include "common/settings.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/perf_stats.h" | 11 | #include "core/perf_stats.h" |
| 12 | #include "input_common/keyboard.h" | 12 | #include "input_common/drivers/keyboard.h" |
| 13 | #include "input_common/drivers/mouse.h" | ||
| 14 | #include "input_common/drivers/touch_screen.h" | ||
| 13 | #include "input_common/main.h" | 15 | #include "input_common/main.h" |
| 14 | #include "input_common/mouse/mouse_input.h" | ||
| 15 | #include "input_common/sdl/sdl.h" | ||
| 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 17 | #include "yuzu_cmd/yuzu_icon.h" | 17 | #include "yuzu_cmd/yuzu_icon.h" |
| 18 | 18 | ||
| @@ -32,42 +32,32 @@ EmuWindow_SDL2::~EmuWindow_SDL2() { | |||
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 34 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { |
| 35 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); | 35 | input_subsystem->GetMouse()->MouseMove(x, y, 0, 0, 0, 0); |
| 36 | |||
| 37 | input_subsystem->GetMouse()->MouseMove(x, y, 0, 0); | ||
| 38 | } | 36 | } |
| 39 | 37 | ||
| 40 | MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { | 38 | InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { |
| 41 | switch (button) { | 39 | switch (button) { |
| 42 | case SDL_BUTTON_LEFT: | 40 | case SDL_BUTTON_LEFT: |
| 43 | return MouseInput::MouseButton::Left; | 41 | return InputCommon::MouseButton::Left; |
| 44 | case SDL_BUTTON_RIGHT: | 42 | case SDL_BUTTON_RIGHT: |
| 45 | return MouseInput::MouseButton::Right; | 43 | return InputCommon::MouseButton::Right; |
| 46 | case SDL_BUTTON_MIDDLE: | 44 | case SDL_BUTTON_MIDDLE: |
| 47 | return MouseInput::MouseButton::Wheel; | 45 | return InputCommon::MouseButton::Wheel; |
| 48 | case SDL_BUTTON_X1: | 46 | case SDL_BUTTON_X1: |
| 49 | return MouseInput::MouseButton::Backward; | 47 | return InputCommon::MouseButton::Backward; |
| 50 | case SDL_BUTTON_X2: | 48 | case SDL_BUTTON_X2: |
| 51 | return MouseInput::MouseButton::Forward; | 49 | return InputCommon::MouseButton::Forward; |
| 52 | default: | 50 | default: |
| 53 | return MouseInput::MouseButton::Undefined; | 51 | return InputCommon::MouseButton::Undefined; |
| 54 | } | 52 | } |
| 55 | } | 53 | } |
| 56 | 54 | ||
| 57 | void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { | 55 | void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { |
| 58 | const auto mouse_button = SDLButtonToMouseButton(button); | 56 | const auto mouse_button = SDLButtonToMouseButton(button); |
| 59 | if (button == SDL_BUTTON_LEFT) { | 57 | if (state == SDL_PRESSED) { |
| 60 | if (state == SDL_PRESSED) { | 58 | input_subsystem->GetMouse()->PressButton(x, y, 0, 0, mouse_button); |
| 61 | TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); | ||
| 62 | } else { | ||
| 63 | TouchReleased(0); | ||
| 64 | } | ||
| 65 | } else { | 59 | } else { |
| 66 | if (state == SDL_PRESSED) { | 60 | input_subsystem->GetMouse()->ReleaseButton(mouse_button); |
| 67 | input_subsystem->GetMouse()->PressButton(x, y, mouse_button); | ||
| 68 | } else { | ||
| 69 | input_subsystem->GetMouse()->ReleaseButton(mouse_button); | ||
| 70 | } | ||
| 71 | } | 61 | } |
| 72 | } | 62 | } |
| 73 | 63 | ||
| @@ -82,29 +72,35 @@ std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, flo | |||
| 82 | static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))}; | 72 | static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))}; |
| 83 | } | 73 | } |
| 84 | 74 | ||
| 85 | void EmuWindow_SDL2::OnFingerDown(float x, float y) { | 75 | void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) { |
| 86 | // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind | 76 | int width, height; |
| 87 | // This isn't critical because the best we can do when we have that is to average them, like the | 77 | SDL_GetWindowSize(render_window, &width, &height); |
| 88 | // 3DS does | ||
| 89 | |||
| 90 | const auto [px, py] = TouchToPixelPos(x, y); | 78 | const auto [px, py] = TouchToPixelPos(x, y); |
| 91 | TouchPressed(px, py, 0); | 79 | const float fx = px * 1.0f / width; |
| 80 | const float fy = py * 1.0f / height; | ||
| 81 | |||
| 82 | input_subsystem->GetTouchScreen()->TouchPressed(fx, fy, id); | ||
| 92 | } | 83 | } |
| 93 | 84 | ||
| 94 | void EmuWindow_SDL2::OnFingerMotion(float x, float y) { | 85 | void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) { |
| 86 | int width, height; | ||
| 87 | SDL_GetWindowSize(render_window, &width, &height); | ||
| 95 | const auto [px, py] = TouchToPixelPos(x, y); | 88 | const auto [px, py] = TouchToPixelPos(x, y); |
| 96 | TouchMoved(px, py, 0); | 89 | const float fx = px * 1.0f / width; |
| 90 | const float fy = py * 1.0f / height; | ||
| 91 | |||
| 92 | input_subsystem->GetTouchScreen()->TouchMoved(fx, fy, id); | ||
| 97 | } | 93 | } |
| 98 | 94 | ||
| 99 | void EmuWindow_SDL2::OnFingerUp() { | 95 | void EmuWindow_SDL2::OnFingerUp() { |
| 100 | TouchReleased(0); | 96 | input_subsystem->GetTouchScreen()->TouchReleased(0); |
| 101 | } | 97 | } |
| 102 | 98 | ||
| 103 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { | 99 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { |
| 104 | if (state == SDL_PRESSED) { | 100 | if (state == SDL_PRESSED) { |
| 105 | input_subsystem->GetKeyboard()->PressKey(key); | 101 | input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key)); |
| 106 | } else if (state == SDL_RELEASED) { | 102 | } else if (state == SDL_RELEASED) { |
| 107 | input_subsystem->GetKeyboard()->ReleaseKey(key); | 103 | input_subsystem->GetKeyboard()->ReleaseKey(static_cast<std::size_t>(key)); |
| 108 | } | 104 | } |
| 109 | } | 105 | } |
| 110 | 106 | ||
| @@ -205,10 +201,12 @@ void EmuWindow_SDL2::WaitEvent() { | |||
| 205 | } | 201 | } |
| 206 | break; | 202 | break; |
| 207 | case SDL_FINGERDOWN: | 203 | case SDL_FINGERDOWN: |
| 208 | OnFingerDown(event.tfinger.x, event.tfinger.y); | 204 | OnFingerDown(event.tfinger.x, event.tfinger.y, |
| 205 | static_cast<std::size_t>(event.tfinger.touchId)); | ||
| 209 | break; | 206 | break; |
| 210 | case SDL_FINGERMOTION: | 207 | case SDL_FINGERMOTION: |
| 211 | OnFingerMotion(event.tfinger.x, event.tfinger.y); | 208 | OnFingerMotion(event.tfinger.x, event.tfinger.y, |
| 209 | static_cast<std::size_t>(event.tfinger.touchId)); | ||
| 212 | break; | 210 | break; |
| 213 | case SDL_FINGERUP: | 211 | case SDL_FINGERUP: |
| 214 | OnFingerUp(); | 212 | OnFingerUp(); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 4810f8775..0af002693 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -16,11 +16,8 @@ class System; | |||
| 16 | 16 | ||
| 17 | namespace InputCommon { | 17 | namespace InputCommon { |
| 18 | class InputSubsystem; | 18 | class InputSubsystem; |
| 19 | } | ||
| 20 | |||
| 21 | namespace MouseInput { | ||
| 22 | enum class MouseButton; | 19 | enum class MouseButton; |
| 23 | } | 20 | } // namespace InputCommon |
| 24 | 21 | ||
| 25 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { | 22 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { |
| 26 | public: | 23 | public: |
| @@ -47,7 +44,7 @@ protected: | |||
| 47 | void OnMouseMotion(s32 x, s32 y); | 44 | void OnMouseMotion(s32 x, s32 y); |
| 48 | 45 | ||
| 49 | /// Converts a SDL mouse button into MouseInput mouse button | 46 | /// Converts a SDL mouse button into MouseInput mouse button |
| 50 | MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const; | 47 | InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const; |
| 51 | 48 | ||
| 52 | /// Called by WaitEvent when a mouse button is pressed or released | 49 | /// Called by WaitEvent when a mouse button is pressed or released |
| 53 | void OnMouseButton(u32 button, u8 state, s32 x, s32 y); | 50 | void OnMouseButton(u32 button, u8 state, s32 x, s32 y); |
| @@ -56,10 +53,10 @@ protected: | |||
| 56 | std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; | 53 | std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; |
| 57 | 54 | ||
| 58 | /// Called by WaitEvent when a finger starts touching the touchscreen | 55 | /// Called by WaitEvent when a finger starts touching the touchscreen |
| 59 | void OnFingerDown(float x, float y); | 56 | void OnFingerDown(float x, float y, std::size_t id); |
| 60 | 57 | ||
| 61 | /// Called by WaitEvent when a finger moves while touching the touchscreen | 58 | /// Called by WaitEvent when a finger moves while touching the touchscreen |
| 62 | void OnFingerMotion(float x, float y); | 59 | void OnFingerMotion(float x, float y, std::size_t id); |
| 63 | 60 | ||
| 64 | /// Called by WaitEvent when a finger stops touching the touchscreen | 61 | /// Called by WaitEvent when a finger stops touching the touchscreen |
| 65 | void OnFingerUp(); | 62 | void OnFingerUp(); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index a075ad08a..70db865ec 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -17,7 +17,6 @@ | |||
| 17 | #include "common/settings.h" | 17 | #include "common/settings.h" |
| 18 | #include "common/string_util.h" | 18 | #include "common/string_util.h" |
| 19 | #include "core/core.h" | 19 | #include "core/core.h" |
| 20 | #include "input_common/keyboard.h" | ||
| 21 | #include "input_common/main.h" | 20 | #include "input_common/main.h" |
| 22 | #include "video_core/renderer_base.h" | 21 | #include "video_core/renderer_base.h" |
| 23 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" | 22 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" |