diff options
110 files changed, 6417 insertions, 729 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index cee720940..b3329318a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -3,12 +3,14 @@ | |||
| 3 | 3 | ||
| 4 | cmake_minimum_required(VERSION 3.22) | 4 | cmake_minimum_required(VERSION 3.22) |
| 5 | 5 | ||
| 6 | project(yuzu) | ||
| 7 | |||
| 6 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") | 8 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") |
| 7 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") | 9 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") |
| 10 | |||
| 8 | include(DownloadExternals) | 11 | include(DownloadExternals) |
| 9 | include(CMakeDependentOption) | 12 | include(CMakeDependentOption) |
| 10 | 13 | include(CTest) | |
| 11 | project(yuzu) | ||
| 12 | 14 | ||
| 13 | # Set bundled sdl2/qt as dependent options. | 15 | # Set bundled sdl2/qt as dependent options. |
| 14 | # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON | 16 | # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON |
| @@ -42,7 +44,7 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) | |||
| 42 | 44 | ||
| 43 | option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) | 45 | option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) |
| 44 | 46 | ||
| 45 | option(YUZU_TESTS "Compile tests" ON) | 47 | option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}") |
| 46 | 48 | ||
| 47 | option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) | 49 | option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) |
| 48 | 50 | ||
| @@ -206,6 +208,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) | |||
| 206 | find_package(enet 1.3 MODULE) | 208 | find_package(enet 1.3 MODULE) |
| 207 | find_package(fmt 9 REQUIRED) | 209 | find_package(fmt 9 REQUIRED) |
| 208 | find_package(inih MODULE) | 210 | find_package(inih MODULE) |
| 211 | find_package(LLVM MODULE) | ||
| 209 | find_package(lz4 REQUIRED) | 212 | find_package(lz4 REQUIRED) |
| 210 | find_package(nlohmann_json 3.8 REQUIRED) | 213 | find_package(nlohmann_json 3.8 REQUIRED) |
| 211 | find_package(Opus 1.3 MODULE) | 214 | find_package(Opus 1.3 MODULE) |
| @@ -242,7 +245,7 @@ if (ENABLE_WEB_SERVICE) | |||
| 242 | endif() | 245 | endif() |
| 243 | 246 | ||
| 244 | if (YUZU_TESTS) | 247 | if (YUZU_TESTS) |
| 245 | find_package(Catch2 2.13.7 REQUIRED) | 248 | find_package(Catch2 3.0.1 REQUIRED) |
| 246 | endif() | 249 | endif() |
| 247 | 250 | ||
| 248 | find_package(Boost 1.73.0 COMPONENTS context) | 251 | find_package(Boost 1.73.0 COMPONENTS context) |
| @@ -606,7 +609,6 @@ if (YUZU_USE_FASTER_LD AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") | |||
| 606 | endif() | 609 | endif() |
| 607 | endif() | 610 | endif() |
| 608 | 611 | ||
| 609 | enable_testing() | ||
| 610 | add_subdirectory(externals) | 612 | add_subdirectory(externals) |
| 611 | add_subdirectory(src) | 613 | add_subdirectory(src) |
| 612 | 614 | ||
diff --git a/CMakeModules/FindLLVM.cmake b/CMakeModules/FindLLVM.cmake new file mode 100644 index 000000000..513d9a536 --- /dev/null +++ b/CMakeModules/FindLLVM.cmake | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf> | ||
| 2 | # | ||
| 3 | # SPDX-License-Identifier: GPL-3.0-or-later | ||
| 4 | |||
| 5 | find_package(LLVM QUIET CONFIG) | ||
| 6 | |||
| 7 | include(FindPackageHandleStandardArgs) | ||
| 8 | find_package_handle_standard_args(LLVM CONFIG_MODE) | ||
| 9 | |||
| 10 | if (LLVM_FOUND AND NOT TARGET LLVM::Demangle) | ||
| 11 | add_library(LLVM::Demangle INTERFACE IMPORTED) | ||
| 12 | llvm_map_components_to_libnames(LLVM_LIBRARIES demangle) | ||
| 13 | target_compile_definitions(LLVM::Demangle INTERFACE ${LLVM_DEFINITIONS}) | ||
| 14 | target_include_directories(LLVM::Demangle INTERFACE ${LLVM_INCLUDE_DIRS}) | ||
| 15 | target_link_libraries(LLVM::Demangle INTERFACE ${LLVM_LIBRARIES}) | ||
| 16 | endif() | ||
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 89a381587..8532fd7a8 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -5,6 +5,9 @@ | |||
| 5 | # some of its variables, which is only possible in 3.13+ | 5 | # some of its variables, which is only possible in 3.13+ |
| 6 | set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) | 6 | set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) |
| 7 | 7 | ||
| 8 | # Disable tests in all externals supporting the standard option name | ||
| 9 | set(BUILD_TESTING OFF) | ||
| 10 | |||
| 8 | # xbyak | 11 | # xbyak |
| 9 | if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak) | 12 | if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak) |
| 10 | add_subdirectory(xbyak EXCLUDE_FROM_ALL) | 13 | add_subdirectory(xbyak EXCLUDE_FROM_ALL) |
| @@ -155,6 +158,9 @@ if (YUZU_USE_EXTERNAL_VULKAN_HEADERS) | |||
| 155 | add_subdirectory(Vulkan-Headers EXCLUDE_FROM_ALL) | 158 | add_subdirectory(Vulkan-Headers EXCLUDE_FROM_ALL) |
| 156 | endif() | 159 | endif() |
| 157 | 160 | ||
| 158 | add_library(demangle STATIC) | 161 | if (NOT TARGET LLVM::Demangle) |
| 159 | target_include_directories(demangle PUBLIC ./demangle) | 162 | add_library(demangle STATIC) |
| 160 | target_sources(demangle PRIVATE demangle/ItaniumDemangle.cpp) | 163 | target_include_directories(demangle PUBLIC ./demangle) |
| 164 | target_sources(demangle PRIVATE demangle/ItaniumDemangle.cpp) | ||
| 165 | add_library(LLVM::Demangle ALIAS demangle) | ||
| 166 | endif() | ||
diff --git a/externals/demangle/ItaniumDemangle.cpp b/externals/demangle/ItaniumDemangle.cpp index 5e078e3e2..b055a2fd7 100644 --- a/externals/demangle/ItaniumDemangle.cpp +++ b/externals/demangle/ItaniumDemangle.cpp | |||
| @@ -11,8 +11,8 @@ | |||
| 11 | // file does not yet support: | 11 | // file does not yet support: |
| 12 | // - C++ modules TS | 12 | // - C++ modules TS |
| 13 | 13 | ||
| 14 | #include "Demangle.h" | 14 | #include "llvm/Demangle/Demangle.h" |
| 15 | #include "ItaniumDemangle.h" | 15 | #include "llvm/Demangle/ItaniumDemangle.h" |
| 16 | 16 | ||
| 17 | #include <cassert> | 17 | #include <cassert> |
| 18 | #include <cctype> | 18 | #include <cctype> |
diff --git a/externals/demangle/Demangle.h b/externals/demangle/llvm/Demangle/Demangle.h index 5b673e4e1..5b673e4e1 100644 --- a/externals/demangle/Demangle.h +++ b/externals/demangle/llvm/Demangle/Demangle.h | |||
diff --git a/externals/demangle/DemangleConfig.h b/externals/demangle/llvm/Demangle/DemangleConfig.h index a8aef9df1..a8aef9df1 100644 --- a/externals/demangle/DemangleConfig.h +++ b/externals/demangle/llvm/Demangle/DemangleConfig.h | |||
diff --git a/externals/demangle/ItaniumDemangle.h b/externals/demangle/llvm/Demangle/ItaniumDemangle.h index 64b35c142..64b35c142 100644 --- a/externals/demangle/ItaniumDemangle.h +++ b/externals/demangle/llvm/Demangle/ItaniumDemangle.h | |||
diff --git a/externals/demangle/StringView.h b/externals/demangle/llvm/Demangle/StringView.h index 44d2b18a3..44d2b18a3 100644 --- a/externals/demangle/StringView.h +++ b/externals/demangle/llvm/Demangle/StringView.h | |||
diff --git a/externals/demangle/Utility.h b/externals/demangle/llvm/Demangle/Utility.h index 50d05c6b1..50d05c6b1 100644 --- a/externals/demangle/Utility.h +++ b/externals/demangle/llvm/Demangle/Utility.h | |||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bd6ac6716..9884a4a0b 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -177,7 +177,7 @@ endif() | |||
| 177 | create_target_directory_groups(common) | 177 | create_target_directory_groups(common) |
| 178 | 178 | ||
| 179 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) | 179 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) |
| 180 | target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd demangle) | 180 | target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle) |
| 181 | 181 | ||
| 182 | if (YUZU_USE_PRECOMPILED_HEADERS) | 182 | if (YUZU_USE_PRECOMPILED_HEADERS) |
| 183 | target_precompile_headers(common PRIVATE precompiled_headers.h) | 183 | target_precompile_headers(common PRIVATE precompiled_headers.h) |
diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp index f4246f666..3310faf86 100644 --- a/src/common/demangle.cpp +++ b/src/common/demangle.cpp | |||
| @@ -1,13 +1,11 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <llvm/Demangle/Demangle.h> | ||
| 5 | |||
| 4 | #include "common/demangle.h" | 6 | #include "common/demangle.h" |
| 5 | #include "common/scope_exit.h" | 7 | #include "common/scope_exit.h" |
| 6 | 8 | ||
| 7 | namespace llvm { | ||
| 8 | char* itaniumDemangle(const char* mangled_name, char* buf, size_t* n, int* status); | ||
| 9 | } | ||
| 10 | |||
| 11 | namespace Common { | 9 | namespace Common { |
| 12 | 10 | ||
| 13 | std::string DemangleSymbol(const std::string& mangled) { | 11 | std::string DemangleSymbol(const std::string& mangled) { |
diff --git a/src/common/input.h b/src/common/input.h index d27b1d772..d61cd7ca8 100644 --- a/src/common/input.h +++ b/src/common/input.h | |||
| @@ -51,6 +51,8 @@ enum class PollingMode { | |||
| 51 | NFC, | 51 | NFC, |
| 52 | // Enable infrared camera polling | 52 | // Enable infrared camera polling |
| 53 | IR, | 53 | IR, |
| 54 | // Enable ring controller polling | ||
| 55 | Ring, | ||
| 54 | }; | 56 | }; |
| 55 | 57 | ||
| 56 | enum class CameraFormat { | 58 | enum class CameraFormat { |
| @@ -62,21 +64,22 @@ enum class CameraFormat { | |||
| 62 | None, | 64 | None, |
| 63 | }; | 65 | }; |
| 64 | 66 | ||
| 65 | // Vibration reply from the controller | 67 | // Different results that can happen from a device request |
| 66 | enum class VibrationError { | 68 | enum class DriverResult { |
| 67 | None, | 69 | Success, |
| 70 | WrongReply, | ||
| 71 | Timeout, | ||
| 72 | UnsupportedControllerType, | ||
| 73 | HandleInUse, | ||
| 74 | ErrorReadingData, | ||
| 75 | ErrorWritingData, | ||
| 76 | NoDeviceDetected, | ||
| 77 | InvalidHandle, | ||
| 68 | NotSupported, | 78 | NotSupported, |
| 69 | Disabled, | 79 | Disabled, |
| 70 | Unknown, | 80 | Unknown, |
| 71 | }; | 81 | }; |
| 72 | 82 | ||
| 73 | // Polling mode reply from the controller | ||
| 74 | enum class PollingError { | ||
| 75 | None, | ||
| 76 | NotSupported, | ||
| 77 | Unknown, | ||
| 78 | }; | ||
| 79 | |||
| 80 | // Nfc reply from the controller | 83 | // Nfc reply from the controller |
| 81 | enum class NfcState { | 84 | enum class NfcState { |
| 82 | Success, | 85 | Success, |
| @@ -90,13 +93,6 @@ enum class NfcState { | |||
| 90 | Unknown, | 93 | Unknown, |
| 91 | }; | 94 | }; |
| 92 | 95 | ||
| 93 | // Ir camera reply from the controller | ||
| 94 | enum class CameraError { | ||
| 95 | None, | ||
| 96 | NotSupported, | ||
| 97 | Unknown, | ||
| 98 | }; | ||
| 99 | |||
| 100 | // Hint for amplification curve to be used | 96 | // Hint for amplification curve to be used |
| 101 | enum class VibrationAmplificationType { | 97 | enum class VibrationAmplificationType { |
| 102 | Linear, | 98 | Linear, |
| @@ -190,6 +186,8 @@ struct TouchStatus { | |||
| 190 | struct BodyColorStatus { | 186 | struct BodyColorStatus { |
| 191 | u32 body{}; | 187 | u32 body{}; |
| 192 | u32 buttons{}; | 188 | u32 buttons{}; |
| 189 | u32 left_grip{}; | ||
| 190 | u32 right_grip{}; | ||
| 193 | }; | 191 | }; |
| 194 | 192 | ||
| 195 | // HD rumble data | 193 | // HD rumble data |
| @@ -228,17 +226,31 @@ enum class ButtonNames { | |||
| 228 | Engine, | 226 | Engine, |
| 229 | // This will display the button by value instead of the button name | 227 | // This will display the button by value instead of the button name |
| 230 | Value, | 228 | Value, |
| 229 | |||
| 230 | // Joycon button names | ||
| 231 | ButtonLeft, | 231 | ButtonLeft, |
| 232 | ButtonRight, | 232 | ButtonRight, |
| 233 | ButtonDown, | 233 | ButtonDown, |
| 234 | ButtonUp, | 234 | ButtonUp, |
| 235 | TriggerZ, | ||
| 236 | TriggerR, | ||
| 237 | TriggerL, | ||
| 238 | ButtonA, | 235 | ButtonA, |
| 239 | ButtonB, | 236 | ButtonB, |
| 240 | ButtonX, | 237 | ButtonX, |
| 241 | ButtonY, | 238 | ButtonY, |
| 239 | ButtonPlus, | ||
| 240 | ButtonMinus, | ||
| 241 | ButtonHome, | ||
| 242 | ButtonCapture, | ||
| 243 | ButtonStickL, | ||
| 244 | ButtonStickR, | ||
| 245 | TriggerL, | ||
| 246 | TriggerZL, | ||
| 247 | TriggerSL, | ||
| 248 | TriggerR, | ||
| 249 | TriggerZR, | ||
| 250 | TriggerSR, | ||
| 251 | |||
| 252 | // GC button names | ||
| 253 | TriggerZ, | ||
| 242 | ButtonStart, | 254 | ButtonStart, |
| 243 | 255 | ||
| 244 | // DS4 button names | 256 | // DS4 button names |
| @@ -316,22 +328,24 @@ class OutputDevice { | |||
| 316 | public: | 328 | public: |
| 317 | virtual ~OutputDevice() = default; | 329 | virtual ~OutputDevice() = default; |
| 318 | 330 | ||
| 319 | virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {} | 331 | virtual DriverResult SetLED([[maybe_unused]] const LedStatus& led_status) { |
| 332 | return DriverResult::NotSupported; | ||
| 333 | } | ||
| 320 | 334 | ||
| 321 | virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { | 335 | virtual DriverResult SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { |
| 322 | return VibrationError::NotSupported; | 336 | return DriverResult::NotSupported; |
| 323 | } | 337 | } |
| 324 | 338 | ||
| 325 | virtual bool IsVibrationEnabled() { | 339 | virtual bool IsVibrationEnabled() { |
| 326 | return false; | 340 | return false; |
| 327 | } | 341 | } |
| 328 | 342 | ||
| 329 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { | 343 | virtual DriverResult SetPollingMode([[maybe_unused]] PollingMode polling_mode) { |
| 330 | return PollingError::NotSupported; | 344 | return DriverResult::NotSupported; |
| 331 | } | 345 | } |
| 332 | 346 | ||
| 333 | virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { | 347 | virtual DriverResult SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { |
| 334 | return CameraError::NotSupported; | 348 | return DriverResult::NotSupported; |
| 335 | } | 349 | } |
| 336 | 350 | ||
| 337 | virtual NfcState SupportsNfc() const { | 351 | virtual NfcState SupportsNfc() const { |
diff --git a/src/common/settings.h b/src/common/settings.h index 80b2eeabc..4b4da4da2 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -483,6 +483,7 @@ struct Values { | |||
| 483 | 483 | ||
| 484 | Setting<bool> enable_raw_input{false, "enable_raw_input"}; | 484 | Setting<bool> enable_raw_input{false, "enable_raw_input"}; |
| 485 | Setting<bool> controller_navigation{true, "controller_navigation"}; | 485 | Setting<bool> controller_navigation{true, "controller_navigation"}; |
| 486 | Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"}; | ||
| 486 | 487 | ||
| 487 | SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; | 488 | SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; |
| 488 | SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; | 489 | SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index a959c9db9..0e06468da 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | #include <common/scope_exit.h> | ||
| 5 | 6 | ||
| 6 | #include "common/polyfill_ranges.h" | 7 | #include "common/polyfill_ranges.h" |
| 7 | #include "common/thread.h" | 8 | #include "common/thread.h" |
| @@ -10,7 +11,6 @@ | |||
| 10 | 11 | ||
| 11 | namespace Core::HID { | 12 | namespace Core::HID { |
| 12 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | 13 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; |
| 13 | constexpr s32 HID_JOYSTICK_MIN = 0x7ffe; | ||
| 14 | constexpr s32 HID_TRIGGER_MAX = 0x7fff; | 14 | constexpr s32 HID_TRIGGER_MAX = 0x7fff; |
| 15 | // Use a common UUID for TAS and Virtual Gamepad | 15 | // Use a common UUID for TAS and Virtual Gamepad |
| 16 | constexpr Common::UUID TAS_UUID = | 16 | constexpr Common::UUID TAS_UUID = |
| @@ -94,6 +94,7 @@ void EmulatedController::ReloadFromSettings() { | |||
| 94 | motion_params[index] = Common::ParamPackage(player.motions[index]); | 94 | motion_params[index] = Common::ParamPackage(player.motions[index]); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | controller.color_values = {}; | ||
| 97 | controller.colors_state.fullkey = { | 98 | controller.colors_state.fullkey = { |
| 98 | .body = GetNpadColor(player.body_color_left), | 99 | .body = GetNpadColor(player.body_color_left), |
| 99 | .button = GetNpadColor(player.button_color_left), | 100 | .button = GetNpadColor(player.button_color_left), |
| @@ -107,6 +108,8 @@ void EmulatedController::ReloadFromSettings() { | |||
| 107 | .button = GetNpadColor(player.button_color_right), | 108 | .button = GetNpadColor(player.button_color_right), |
| 108 | }; | 109 | }; |
| 109 | 110 | ||
| 111 | ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); | ||
| 112 | |||
| 110 | // Other or debug controller should always be a pro controller | 113 | // Other or debug controller should always be a pro controller |
| 111 | if (npad_id_type != NpadIdType::Other) { | 114 | if (npad_id_type != NpadIdType::Other) { |
| 112 | SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); | 115 | SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); |
| @@ -133,18 +136,28 @@ void EmulatedController::LoadDevices() { | |||
| 133 | trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; | 136 | trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; |
| 134 | trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; | 137 | trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; |
| 135 | 138 | ||
| 139 | color_params[LeftIndex] = left_joycon; | ||
| 140 | color_params[RightIndex] = right_joycon; | ||
| 141 | color_params[LeftIndex].Set("color", true); | ||
| 142 | color_params[RightIndex].Set("color", true); | ||
| 143 | |||
| 136 | battery_params[LeftIndex] = left_joycon; | 144 | battery_params[LeftIndex] = left_joycon; |
| 137 | battery_params[RightIndex] = right_joycon; | 145 | battery_params[RightIndex] = right_joycon; |
| 138 | battery_params[LeftIndex].Set("battery", true); | 146 | battery_params[LeftIndex].Set("battery", true); |
| 139 | battery_params[RightIndex].Set("battery", true); | 147 | battery_params[RightIndex].Set("battery", true); |
| 140 | 148 | ||
| 141 | camera_params = Common::ParamPackage{"engine:camera,camera:1"}; | 149 | camera_params[0] = right_joycon; |
| 142 | nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; | 150 | camera_params[0].Set("camera", true); |
| 151 | camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; | ||
| 152 | ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; | ||
| 153 | nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; | ||
| 154 | nfc_params[1] = right_joycon; | ||
| 155 | nfc_params[1].Set("nfc", true); | ||
| 143 | 156 | ||
| 144 | output_params[LeftIndex] = left_joycon; | 157 | output_params[LeftIndex] = left_joycon; |
| 145 | output_params[RightIndex] = right_joycon; | 158 | output_params[RightIndex] = right_joycon; |
| 146 | output_params[2] = camera_params; | 159 | output_params[2] = camera_params[1]; |
| 147 | output_params[3] = nfc_params; | 160 | output_params[3] = nfc_params[0]; |
| 148 | output_params[LeftIndex].Set("output", true); | 161 | output_params[LeftIndex].Set("output", true); |
| 149 | output_params[RightIndex].Set("output", true); | 162 | output_params[RightIndex].Set("output", true); |
| 150 | output_params[2].Set("output", true); | 163 | output_params[2].Set("output", true); |
| @@ -160,8 +173,11 @@ void EmulatedController::LoadDevices() { | |||
| 160 | Common::Input::CreateInputDevice); | 173 | Common::Input::CreateInputDevice); |
| 161 | std::ranges::transform(battery_params, battery_devices.begin(), | 174 | std::ranges::transform(battery_params, battery_devices.begin(), |
| 162 | Common::Input::CreateInputDevice); | 175 | Common::Input::CreateInputDevice); |
| 163 | camera_devices = Common::Input::CreateInputDevice(camera_params); | 176 | std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice); |
| 164 | nfc_devices = Common::Input::CreateInputDevice(nfc_params); | 177 | std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice); |
| 178 | std::ranges::transform(ring_params, ring_analog_devices.begin(), | ||
| 179 | Common::Input::CreateInputDevice); | ||
| 180 | std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice); | ||
| 165 | std::ranges::transform(output_params, output_devices.begin(), | 181 | std::ranges::transform(output_params, output_devices.begin(), |
| 166 | Common::Input::CreateOutputDevice); | 182 | Common::Input::CreateOutputDevice); |
| 167 | 183 | ||
| @@ -323,6 +339,19 @@ void EmulatedController::ReloadInput() { | |||
| 323 | battery_devices[index]->ForceUpdate(); | 339 | battery_devices[index]->ForceUpdate(); |
| 324 | } | 340 | } |
| 325 | 341 | ||
| 342 | for (std::size_t index = 0; index < color_devices.size(); ++index) { | ||
| 343 | if (!color_devices[index]) { | ||
| 344 | continue; | ||
| 345 | } | ||
| 346 | color_devices[index]->SetCallback({ | ||
| 347 | .on_change = | ||
| 348 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 349 | SetColors(callback, index); | ||
| 350 | }, | ||
| 351 | }); | ||
| 352 | color_devices[index]->ForceUpdate(); | ||
| 353 | } | ||
| 354 | |||
| 326 | for (std::size_t index = 0; index < motion_devices.size(); ++index) { | 355 | for (std::size_t index = 0; index < motion_devices.size(); ++index) { |
| 327 | if (!motion_devices[index]) { | 356 | if (!motion_devices[index]) { |
| 328 | continue; | 357 | continue; |
| @@ -336,22 +365,37 @@ void EmulatedController::ReloadInput() { | |||
| 336 | motion_devices[index]->ForceUpdate(); | 365 | motion_devices[index]->ForceUpdate(); |
| 337 | } | 366 | } |
| 338 | 367 | ||
| 339 | if (camera_devices) { | 368 | for (std::size_t index = 0; index < camera_devices.size(); ++index) { |
| 340 | camera_devices->SetCallback({ | 369 | if (!camera_devices[index]) { |
| 370 | continue; | ||
| 371 | } | ||
| 372 | camera_devices[index]->SetCallback({ | ||
| 341 | .on_change = | 373 | .on_change = |
| 342 | [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, | 374 | [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, |
| 343 | }); | 375 | }); |
| 344 | camera_devices->ForceUpdate(); | 376 | camera_devices[index]->ForceUpdate(); |
| 345 | } | 377 | } |
| 346 | 378 | ||
| 347 | if (nfc_devices) { | 379 | for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) { |
| 348 | if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { | 380 | if (!ring_analog_devices[index]) { |
| 349 | nfc_devices->SetCallback({ | 381 | continue; |
| 350 | .on_change = | ||
| 351 | [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, | ||
| 352 | }); | ||
| 353 | nfc_devices->ForceUpdate(); | ||
| 354 | } | 382 | } |
| 383 | ring_analog_devices[index]->SetCallback({ | ||
| 384 | .on_change = | ||
| 385 | [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); }, | ||
| 386 | }); | ||
| 387 | ring_analog_devices[index]->ForceUpdate(); | ||
| 388 | } | ||
| 389 | |||
| 390 | for (std::size_t index = 0; index < nfc_devices.size(); ++index) { | ||
| 391 | if (!nfc_devices[index]) { | ||
| 392 | continue; | ||
| 393 | } | ||
| 394 | nfc_devices[index]->SetCallback({ | ||
| 395 | .on_change = | ||
| 396 | [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, | ||
| 397 | }); | ||
| 398 | nfc_devices[index]->ForceUpdate(); | ||
| 355 | } | 399 | } |
| 356 | 400 | ||
| 357 | // Register TAS devices. No need to force update | 401 | // Register TAS devices. No need to force update |
| @@ -421,6 +465,9 @@ void EmulatedController::UnloadInput() { | |||
| 421 | for (auto& battery : battery_devices) { | 465 | for (auto& battery : battery_devices) { |
| 422 | battery.reset(); | 466 | battery.reset(); |
| 423 | } | 467 | } |
| 468 | for (auto& color : color_devices) { | ||
| 469 | color.reset(); | ||
| 470 | } | ||
| 424 | for (auto& output : output_devices) { | 471 | for (auto& output : output_devices) { |
| 425 | output.reset(); | 472 | output.reset(); |
| 426 | } | 473 | } |
| @@ -436,8 +483,15 @@ void EmulatedController::UnloadInput() { | |||
| 436 | for (auto& stick : virtual_stick_devices) { | 483 | for (auto& stick : virtual_stick_devices) { |
| 437 | stick.reset(); | 484 | stick.reset(); |
| 438 | } | 485 | } |
| 439 | camera_devices.reset(); | 486 | for (auto& camera : camera_devices) { |
| 440 | nfc_devices.reset(); | 487 | camera.reset(); |
| 488 | } | ||
| 489 | for (auto& ring : ring_analog_devices) { | ||
| 490 | ring.reset(); | ||
| 491 | } | ||
| 492 | for (auto& nfc : nfc_devices) { | ||
| 493 | nfc.reset(); | ||
| 494 | } | ||
| 441 | } | 495 | } |
| 442 | 496 | ||
| 443 | void EmulatedController::EnableConfiguration() { | 497 | void EmulatedController::EnableConfiguration() { |
| @@ -449,6 +503,11 @@ void EmulatedController::EnableConfiguration() { | |||
| 449 | void EmulatedController::DisableConfiguration() { | 503 | void EmulatedController::DisableConfiguration() { |
| 450 | is_configuring = false; | 504 | is_configuring = false; |
| 451 | 505 | ||
| 506 | // Get Joycon colors before turning on the controller | ||
| 507 | for (const auto& color_device : color_devices) { | ||
| 508 | color_device->ForceUpdate(); | ||
| 509 | } | ||
| 510 | |||
| 452 | // Apply temporary npad type to the real controller | 511 | // Apply temporary npad type to the real controller |
| 453 | if (tmp_npad_type != npad_type) { | 512 | if (tmp_npad_type != npad_type) { |
| 454 | if (is_connected) { | 513 | if (is_connected) { |
| @@ -502,6 +561,9 @@ void EmulatedController::SaveCurrentConfig() { | |||
| 502 | for (std::size_t index = 0; index < player.motions.size(); ++index) { | 561 | for (std::size_t index = 0; index < player.motions.size(); ++index) { |
| 503 | player.motions[index] = motion_params[index].Serialize(); | 562 | player.motions[index] = motion_params[index].Serialize(); |
| 504 | } | 563 | } |
| 564 | if (npad_id_type == NpadIdType::Player1) { | ||
| 565 | Settings::values.ringcon_analogs = ring_params[0].Serialize(); | ||
| 566 | } | ||
| 505 | } | 567 | } |
| 506 | 568 | ||
| 507 | void EmulatedController::RestoreConfig() { | 569 | void EmulatedController::RestoreConfig() { |
| @@ -773,17 +835,21 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, | |||
| 773 | if (index >= controller.stick_values.size()) { | 835 | if (index >= controller.stick_values.size()) { |
| 774 | return; | 836 | return; |
| 775 | } | 837 | } |
| 776 | std::unique_lock lock{mutex}; | 838 | auto trigger_guard = |
| 839 | SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); }); | ||
| 840 | std::scoped_lock lock{mutex}; | ||
| 777 | const auto stick_value = TransformToStick(callback); | 841 | const auto stick_value = TransformToStick(callback); |
| 778 | 842 | ||
| 779 | // Only read stick values that have the same uuid or are over the threshold to avoid flapping | 843 | // Only read stick values that have the same uuid or are over the threshold to avoid flapping |
| 780 | if (controller.stick_values[index].uuid != uuid) { | 844 | if (controller.stick_values[index].uuid != uuid) { |
| 781 | const bool is_tas = uuid == TAS_UUID; | 845 | const bool is_tas = uuid == TAS_UUID; |
| 782 | if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) { | 846 | if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) { |
| 847 | trigger_guard.Cancel(); | ||
| 783 | return; | 848 | return; |
| 784 | } | 849 | } |
| 785 | if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left && | 850 | if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left && |
| 786 | !stick_value.right) { | 851 | !stick_value.right) { |
| 852 | trigger_guard.Cancel(); | ||
| 787 | return; | 853 | return; |
| 788 | } | 854 | } |
| 789 | } | 855 | } |
| @@ -794,21 +860,12 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, | |||
| 794 | if (is_configuring) { | 860 | if (is_configuring) { |
| 795 | controller.analog_stick_state.left = {}; | 861 | controller.analog_stick_state.left = {}; |
| 796 | controller.analog_stick_state.right = {}; | 862 | controller.analog_stick_state.right = {}; |
| 797 | lock.unlock(); | ||
| 798 | TriggerOnChange(ControllerTriggerType::Stick, false); | ||
| 799 | return; | 863 | return; |
| 800 | } | 864 | } |
| 801 | 865 | ||
| 802 | const auto FloatToShort = [](float a) { | ||
| 803 | if (a > 0) { | ||
| 804 | return static_cast<s32>(a * HID_JOYSTICK_MAX); | ||
| 805 | } | ||
| 806 | return static_cast<s32>(a * HID_JOYSTICK_MIN); | ||
| 807 | }; | ||
| 808 | |||
| 809 | const AnalogStickState stick{ | 866 | const AnalogStickState stick{ |
| 810 | .x = FloatToShort(controller.stick_values[index].x.value), | 867 | .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX), |
| 811 | .y = FloatToShort(controller.stick_values[index].y.value), | 868 | .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX), |
| 812 | }; | 869 | }; |
| 813 | 870 | ||
| 814 | switch (index) { | 871 | switch (index) { |
| @@ -827,9 +884,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, | |||
| 827 | controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); | 884 | controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); |
| 828 | break; | 885 | break; |
| 829 | } | 886 | } |
| 830 | |||
| 831 | lock.unlock(); | ||
| 832 | TriggerOnChange(ControllerTriggerType::Stick, true); | ||
| 833 | } | 887 | } |
| 834 | 888 | ||
| 835 | void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback, | 889 | void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback, |
| @@ -837,7 +891,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac | |||
| 837 | if (index >= controller.trigger_values.size()) { | 891 | if (index >= controller.trigger_values.size()) { |
| 838 | return; | 892 | return; |
| 839 | } | 893 | } |
| 840 | std::unique_lock lock{mutex}; | 894 | auto trigger_guard = |
| 895 | SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); }); | ||
| 896 | std::scoped_lock lock{mutex}; | ||
| 841 | const auto trigger_value = TransformToTrigger(callback); | 897 | const auto trigger_value = TransformToTrigger(callback); |
| 842 | 898 | ||
| 843 | // Only read trigger values that have the same uuid or are pressed once | 899 | // Only read trigger values that have the same uuid or are pressed once |
| @@ -853,13 +909,12 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac | |||
| 853 | if (is_configuring) { | 909 | if (is_configuring) { |
| 854 | controller.gc_trigger_state.left = 0; | 910 | controller.gc_trigger_state.left = 0; |
| 855 | controller.gc_trigger_state.right = 0; | 911 | controller.gc_trigger_state.right = 0; |
| 856 | lock.unlock(); | ||
| 857 | TriggerOnChange(ControllerTriggerType::Trigger, false); | ||
| 858 | return; | 912 | return; |
| 859 | } | 913 | } |
| 860 | 914 | ||
| 861 | // Only GC controllers have analog triggers | 915 | // Only GC controllers have analog triggers |
| 862 | if (npad_type != NpadStyleIndex::GameCube) { | 916 | if (npad_type != NpadStyleIndex::GameCube) { |
| 917 | trigger_guard.Cancel(); | ||
| 863 | return; | 918 | return; |
| 864 | } | 919 | } |
| 865 | 920 | ||
| @@ -876,9 +931,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac | |||
| 876 | controller.npad_button_state.zr.Assign(trigger.pressed.value); | 931 | controller.npad_button_state.zr.Assign(trigger.pressed.value); |
| 877 | break; | 932 | break; |
| 878 | } | 933 | } |
| 879 | |||
| 880 | lock.unlock(); | ||
| 881 | TriggerOnChange(ControllerTriggerType::Trigger, true); | ||
| 882 | } | 934 | } |
| 883 | 935 | ||
| 884 | void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback, | 936 | void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback, |
| @@ -886,7 +938,8 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback | |||
| 886 | if (index >= controller.motion_values.size()) { | 938 | if (index >= controller.motion_values.size()) { |
| 887 | return; | 939 | return; |
| 888 | } | 940 | } |
| 889 | std::unique_lock lock{mutex}; | 941 | SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); }); |
| 942 | std::scoped_lock lock{mutex}; | ||
| 890 | auto& raw_status = controller.motion_values[index].raw_status; | 943 | auto& raw_status = controller.motion_values[index].raw_status; |
| 891 | auto& emulated = controller.motion_values[index].emulated; | 944 | auto& emulated = controller.motion_values[index].emulated; |
| 892 | 945 | ||
| @@ -907,8 +960,6 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback | |||
| 907 | force_update_motion = raw_status.force_update; | 960 | force_update_motion = raw_status.force_update; |
| 908 | 961 | ||
| 909 | if (is_configuring) { | 962 | if (is_configuring) { |
| 910 | lock.unlock(); | ||
| 911 | TriggerOnChange(ControllerTriggerType::Motion, false); | ||
| 912 | return; | 963 | return; |
| 913 | } | 964 | } |
| 914 | 965 | ||
| @@ -918,9 +969,56 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback | |||
| 918 | motion.rotation = emulated.GetRotations(); | 969 | motion.rotation = emulated.GetRotations(); |
| 919 | motion.orientation = emulated.GetOrientation(); | 970 | motion.orientation = emulated.GetOrientation(); |
| 920 | motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); | 971 | motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); |
| 972 | } | ||
| 921 | 973 | ||
| 922 | lock.unlock(); | 974 | void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback, |
| 923 | TriggerOnChange(ControllerTriggerType::Motion, true); | 975 | std::size_t index) { |
| 976 | if (index >= controller.color_values.size()) { | ||
| 977 | return; | ||
| 978 | } | ||
| 979 | auto trigger_guard = | ||
| 980 | SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); }); | ||
| 981 | std::scoped_lock lock{mutex}; | ||
| 982 | controller.color_values[index] = TransformToColor(callback); | ||
| 983 | |||
| 984 | if (is_configuring) { | ||
| 985 | return; | ||
| 986 | } | ||
| 987 | |||
| 988 | if (controller.color_values[index].body == 0) { | ||
| 989 | trigger_guard.Cancel(); | ||
| 990 | return; | ||
| 991 | } | ||
| 992 | |||
| 993 | controller.colors_state.fullkey = { | ||
| 994 | .body = GetNpadColor(controller.color_values[index].body), | ||
| 995 | .button = GetNpadColor(controller.color_values[index].buttons), | ||
| 996 | }; | ||
| 997 | if (npad_type == NpadStyleIndex::ProController) { | ||
| 998 | controller.colors_state.left = { | ||
| 999 | .body = GetNpadColor(controller.color_values[index].left_grip), | ||
| 1000 | .button = GetNpadColor(controller.color_values[index].buttons), | ||
| 1001 | }; | ||
| 1002 | controller.colors_state.right = { | ||
| 1003 | .body = GetNpadColor(controller.color_values[index].right_grip), | ||
| 1004 | .button = GetNpadColor(controller.color_values[index].buttons), | ||
| 1005 | }; | ||
| 1006 | } else { | ||
| 1007 | switch (index) { | ||
| 1008 | case LeftIndex: | ||
| 1009 | controller.colors_state.left = { | ||
| 1010 | .body = GetNpadColor(controller.color_values[index].body), | ||
| 1011 | .button = GetNpadColor(controller.color_values[index].buttons), | ||
| 1012 | }; | ||
| 1013 | break; | ||
| 1014 | case RightIndex: | ||
| 1015 | controller.colors_state.right = { | ||
| 1016 | .body = GetNpadColor(controller.color_values[index].body), | ||
| 1017 | .button = GetNpadColor(controller.color_values[index].buttons), | ||
| 1018 | }; | ||
| 1019 | break; | ||
| 1020 | } | ||
| 1021 | } | ||
| 924 | } | 1022 | } |
| 925 | 1023 | ||
| 926 | void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, | 1024 | void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, |
| @@ -928,12 +1026,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac | |||
| 928 | if (index >= controller.battery_values.size()) { | 1026 | if (index >= controller.battery_values.size()) { |
| 929 | return; | 1027 | return; |
| 930 | } | 1028 | } |
| 931 | std::unique_lock lock{mutex}; | 1029 | SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); }); |
| 1030 | std::scoped_lock lock{mutex}; | ||
| 932 | controller.battery_values[index] = TransformToBattery(callback); | 1031 | controller.battery_values[index] = TransformToBattery(callback); |
| 933 | 1032 | ||
| 934 | if (is_configuring) { | 1033 | if (is_configuring) { |
| 935 | lock.unlock(); | ||
| 936 | TriggerOnChange(ControllerTriggerType::Battery, false); | ||
| 937 | return; | 1034 | return; |
| 938 | } | 1035 | } |
| 939 | 1036 | ||
| @@ -989,18 +1086,14 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac | |||
| 989 | }; | 1086 | }; |
| 990 | break; | 1087 | break; |
| 991 | } | 1088 | } |
| 992 | |||
| 993 | lock.unlock(); | ||
| 994 | TriggerOnChange(ControllerTriggerType::Battery, true); | ||
| 995 | } | 1089 | } |
| 996 | 1090 | ||
| 997 | void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { | 1091 | void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { |
| 998 | std::unique_lock lock{mutex}; | 1092 | SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); }); |
| 1093 | std::scoped_lock lock{mutex}; | ||
| 999 | controller.camera_values = TransformToCamera(callback); | 1094 | controller.camera_values = TransformToCamera(callback); |
| 1000 | 1095 | ||
| 1001 | if (is_configuring) { | 1096 | if (is_configuring) { |
| 1002 | lock.unlock(); | ||
| 1003 | TriggerOnChange(ControllerTriggerType::IrSensor, false); | ||
| 1004 | return; | 1097 | return; |
| 1005 | } | 1098 | } |
| 1006 | 1099 | ||
| @@ -1008,18 +1101,28 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback | |||
| 1008 | controller.camera_state.format = | 1101 | controller.camera_state.format = |
| 1009 | static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); | 1102 | static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); |
| 1010 | controller.camera_state.data = controller.camera_values.data; | 1103 | controller.camera_state.data = controller.camera_values.data; |
| 1104 | } | ||
| 1011 | 1105 | ||
| 1012 | lock.unlock(); | 1106 | void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) { |
| 1013 | TriggerOnChange(ControllerTriggerType::IrSensor, true); | 1107 | SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); }); |
| 1108 | std::scoped_lock lock{mutex}; | ||
| 1109 | const auto force_value = TransformToStick(callback); | ||
| 1110 | |||
| 1111 | controller.ring_analog_value = force_value.x; | ||
| 1112 | |||
| 1113 | if (is_configuring) { | ||
| 1114 | return; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | controller.ring_analog_state.force = force_value.x.value; | ||
| 1014 | } | 1118 | } |
| 1015 | 1119 | ||
| 1016 | void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { | 1120 | void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { |
| 1017 | std::unique_lock lock{mutex}; | 1121 | SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); }); |
| 1122 | std::scoped_lock lock{mutex}; | ||
| 1018 | controller.nfc_values = TransformToNfc(callback); | 1123 | controller.nfc_values = TransformToNfc(callback); |
| 1019 | 1124 | ||
| 1020 | if (is_configuring) { | 1125 | if (is_configuring) { |
| 1021 | lock.unlock(); | ||
| 1022 | TriggerOnChange(ControllerTriggerType::Nfc, false); | ||
| 1023 | return; | 1126 | return; |
| 1024 | } | 1127 | } |
| 1025 | 1128 | ||
| @@ -1027,9 +1130,6 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { | |||
| 1027 | controller.nfc_values.state, | 1130 | controller.nfc_values.state, |
| 1028 | controller.nfc_values.data, | 1131 | controller.nfc_values.data, |
| 1029 | }; | 1132 | }; |
| 1030 | |||
| 1031 | lock.unlock(); | ||
| 1032 | TriggerOnChange(ControllerTriggerType::Nfc, true); | ||
| 1033 | } | 1133 | } |
| 1034 | 1134 | ||
| 1035 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { | 1135 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { |
| @@ -1061,7 +1161,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v | |||
| 1061 | .type = type, | 1161 | .type = type, |
| 1062 | }; | 1162 | }; |
| 1063 | return output_devices[device_index]->SetVibration(status) == | 1163 | return output_devices[device_index]->SetVibration(status) == |
| 1064 | Common::Input::VibrationError::None; | 1164 | Common::Input::DriverResult::Success; |
| 1065 | } | 1165 | } |
| 1066 | 1166 | ||
| 1067 | bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { | 1167 | bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { |
| @@ -1083,16 +1183,32 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { | |||
| 1083 | return output_devices[device_index]->IsVibrationEnabled(); | 1183 | return output_devices[device_index]->IsVibrationEnabled(); |
| 1084 | } | 1184 | } |
| 1085 | 1185 | ||
| 1086 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | 1186 | Common::Input::DriverResult EmulatedController::SetPollingMode( |
| 1087 | LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); | 1187 | EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { |
| 1088 | auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1188 | LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); |
| 1189 | |||
| 1190 | auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; | ||
| 1191 | auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | ||
| 1089 | auto& nfc_output_device = output_devices[3]; | 1192 | auto& nfc_output_device = output_devices[3]; |
| 1090 | 1193 | ||
| 1091 | const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); | 1194 | if (device_index == EmulatedDeviceIndex::LeftIndex) { |
| 1092 | const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); | 1195 | return left_output_device->SetPollingMode(polling_mode); |
| 1196 | } | ||
| 1197 | |||
| 1198 | if (device_index == EmulatedDeviceIndex::RightIndex) { | ||
| 1199 | const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); | ||
| 1200 | const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); | ||
| 1201 | |||
| 1202 | if (virtual_nfc_result == Common::Input::DriverResult::Success) { | ||
| 1203 | return virtual_nfc_result; | ||
| 1204 | } | ||
| 1205 | return mapped_nfc_result; | ||
| 1206 | } | ||
| 1093 | 1207 | ||
| 1094 | return virtual_nfc_result == Common::Input::PollingError::None || | 1208 | left_output_device->SetPollingMode(polling_mode); |
| 1095 | mapped_nfc_result == Common::Input::PollingError::None; | 1209 | right_output_device->SetPollingMode(polling_mode); |
| 1210 | nfc_output_device->SetPollingMode(polling_mode); | ||
| 1211 | return Common::Input::DriverResult::Success; | ||
| 1096 | } | 1212 | } |
| 1097 | 1213 | ||
| 1098 | bool EmulatedController::SetCameraFormat( | 1214 | bool EmulatedController::SetCameraFormat( |
| @@ -1103,13 +1219,22 @@ bool EmulatedController::SetCameraFormat( | |||
| 1103 | auto& camera_output_device = output_devices[2]; | 1219 | auto& camera_output_device = output_devices[2]; |
| 1104 | 1220 | ||
| 1105 | if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( | 1221 | if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( |
| 1106 | camera_format)) == Common::Input::CameraError::None) { | 1222 | camera_format)) == Common::Input::DriverResult::Success) { |
| 1107 | return true; | 1223 | return true; |
| 1108 | } | 1224 | } |
| 1109 | 1225 | ||
| 1110 | // Fallback to Qt camera if native device doesn't have support | 1226 | // Fallback to Qt camera if native device doesn't have support |
| 1111 | return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( | 1227 | return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( |
| 1112 | camera_format)) == Common::Input::CameraError::None; | 1228 | camera_format)) == Common::Input::DriverResult::Success; |
| 1229 | } | ||
| 1230 | |||
| 1231 | Common::ParamPackage EmulatedController::GetRingParam() const { | ||
| 1232 | return ring_params[0]; | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | void EmulatedController::SetRingParam(Common::ParamPackage param) { | ||
| 1236 | ring_params[0] = std::move(param); | ||
| 1237 | ReloadInput(); | ||
| 1113 | } | 1238 | } |
| 1114 | 1239 | ||
| 1115 | bool EmulatedController::HasNfc() const { | 1240 | bool EmulatedController::HasNfc() const { |
| @@ -1263,39 +1388,35 @@ void EmulatedController::Connect(bool use_temporary_value) { | |||
| 1263 | return; | 1388 | return; |
| 1264 | } | 1389 | } |
| 1265 | 1390 | ||
| 1266 | std::unique_lock lock{mutex}; | 1391 | auto trigger_guard = |
| 1392 | SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); }); | ||
| 1393 | std::scoped_lock lock{mutex}; | ||
| 1267 | if (is_configuring) { | 1394 | if (is_configuring) { |
| 1268 | tmp_is_connected = true; | 1395 | tmp_is_connected = true; |
| 1269 | lock.unlock(); | ||
| 1270 | TriggerOnChange(ControllerTriggerType::Connected, false); | ||
| 1271 | return; | 1396 | return; |
| 1272 | } | 1397 | } |
| 1273 | 1398 | ||
| 1274 | if (is_connected) { | 1399 | if (is_connected) { |
| 1400 | trigger_guard.Cancel(); | ||
| 1275 | return; | 1401 | return; |
| 1276 | } | 1402 | } |
| 1277 | is_connected = true; | 1403 | is_connected = true; |
| 1278 | |||
| 1279 | lock.unlock(); | ||
| 1280 | TriggerOnChange(ControllerTriggerType::Connected, true); | ||
| 1281 | } | 1404 | } |
| 1282 | 1405 | ||
| 1283 | void EmulatedController::Disconnect() { | 1406 | void EmulatedController::Disconnect() { |
| 1284 | std::unique_lock lock{mutex}; | 1407 | auto trigger_guard = |
| 1408 | SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); }); | ||
| 1409 | std::scoped_lock lock{mutex}; | ||
| 1285 | if (is_configuring) { | 1410 | if (is_configuring) { |
| 1286 | tmp_is_connected = false; | 1411 | tmp_is_connected = false; |
| 1287 | lock.unlock(); | ||
| 1288 | TriggerOnChange(ControllerTriggerType::Disconnected, false); | ||
| 1289 | return; | 1412 | return; |
| 1290 | } | 1413 | } |
| 1291 | 1414 | ||
| 1292 | if (!is_connected) { | 1415 | if (!is_connected) { |
| 1416 | trigger_guard.Cancel(); | ||
| 1293 | return; | 1417 | return; |
| 1294 | } | 1418 | } |
| 1295 | is_connected = false; | 1419 | is_connected = false; |
| 1296 | |||
| 1297 | lock.unlock(); | ||
| 1298 | TriggerOnChange(ControllerTriggerType::Disconnected, true); | ||
| 1299 | } | 1420 | } |
| 1300 | 1421 | ||
| 1301 | bool EmulatedController::IsConnected(bool get_temporary_value) const { | 1422 | bool EmulatedController::IsConnected(bool get_temporary_value) const { |
| @@ -1320,19 +1441,21 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c | |||
| 1320 | } | 1441 | } |
| 1321 | 1442 | ||
| 1322 | void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { | 1443 | void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { |
| 1323 | std::unique_lock lock{mutex}; | 1444 | auto trigger_guard = |
| 1445 | SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); }); | ||
| 1446 | std::scoped_lock lock{mutex}; | ||
| 1324 | 1447 | ||
| 1325 | if (is_configuring) { | 1448 | if (is_configuring) { |
| 1326 | if (tmp_npad_type == npad_type_) { | 1449 | if (tmp_npad_type == npad_type_) { |
| 1450 | trigger_guard.Cancel(); | ||
| 1327 | return; | 1451 | return; |
| 1328 | } | 1452 | } |
| 1329 | tmp_npad_type = npad_type_; | 1453 | tmp_npad_type = npad_type_; |
| 1330 | lock.unlock(); | ||
| 1331 | TriggerOnChange(ControllerTriggerType::Type, false); | ||
| 1332 | return; | 1454 | return; |
| 1333 | } | 1455 | } |
| 1334 | 1456 | ||
| 1335 | if (npad_type == npad_type_) { | 1457 | if (npad_type == npad_type_) { |
| 1458 | trigger_guard.Cancel(); | ||
| 1336 | return; | 1459 | return; |
| 1337 | } | 1460 | } |
| 1338 | if (is_connected) { | 1461 | if (is_connected) { |
| @@ -1340,9 +1463,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { | |||
| 1340 | NpadIdTypeToIndex(npad_id_type)); | 1463 | NpadIdTypeToIndex(npad_id_type)); |
| 1341 | } | 1464 | } |
| 1342 | npad_type = npad_type_; | 1465 | npad_type = npad_type_; |
| 1343 | |||
| 1344 | lock.unlock(); | ||
| 1345 | TriggerOnChange(ControllerTriggerType::Type, true); | ||
| 1346 | } | 1466 | } |
| 1347 | 1467 | ||
| 1348 | LedPattern EmulatedController::GetLedPattern() const { | 1468 | LedPattern EmulatedController::GetLedPattern() const { |
| @@ -1403,6 +1523,10 @@ CameraValues EmulatedController::GetCameraValues() const { | |||
| 1403 | return controller.camera_values; | 1523 | return controller.camera_values; |
| 1404 | } | 1524 | } |
| 1405 | 1525 | ||
| 1526 | RingAnalogValue EmulatedController::GetRingSensorValues() const { | ||
| 1527 | return controller.ring_analog_value; | ||
| 1528 | } | ||
| 1529 | |||
| 1406 | HomeButtonState EmulatedController::GetHomeButtons() const { | 1530 | HomeButtonState EmulatedController::GetHomeButtons() const { |
| 1407 | std::scoped_lock lock{mutex}; | 1531 | std::scoped_lock lock{mutex}; |
| 1408 | if (is_configuring) { | 1532 | if (is_configuring) { |
| @@ -1436,7 +1560,7 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const { | |||
| 1436 | } | 1560 | } |
| 1437 | 1561 | ||
| 1438 | AnalogSticks EmulatedController::GetSticks() const { | 1562 | AnalogSticks EmulatedController::GetSticks() const { |
| 1439 | std::unique_lock lock{mutex}; | 1563 | std::scoped_lock lock{mutex}; |
| 1440 | 1564 | ||
| 1441 | if (is_configuring) { | 1565 | if (is_configuring) { |
| 1442 | return {}; | 1566 | return {}; |
| @@ -1486,6 +1610,10 @@ const CameraState& EmulatedController::GetCamera() const { | |||
| 1486 | return controller.camera_state; | 1610 | return controller.camera_state; |
| 1487 | } | 1611 | } |
| 1488 | 1612 | ||
| 1613 | RingSensorForce EmulatedController::GetRingSensorForce() const { | ||
| 1614 | return controller.ring_analog_state; | ||
| 1615 | } | ||
| 1616 | |||
| 1489 | const NfcState& EmulatedController::GetNfc() const { | 1617 | const NfcState& EmulatedController::GetNfc() const { |
| 1490 | std::scoped_lock lock{mutex}; | 1618 | std::scoped_lock lock{mutex}; |
| 1491 | return controller.nfc_state; | 1619 | return controller.nfc_state; |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index a398543a6..3ac77b2b5 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -35,19 +35,27 @@ using ControllerMotionDevices = | |||
| 35 | std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; | 35 | std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; |
| 36 | using TriggerDevices = | 36 | using TriggerDevices = |
| 37 | std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; | 37 | std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; |
| 38 | using ColorDevices = | ||
| 39 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 38 | using BatteryDevices = | 40 | using BatteryDevices = |
| 39 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | 41 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; |
| 40 | using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; | 42 | using CameraDevices = |
| 41 | using NfcDevices = std::unique_ptr<Common::Input::InputDevice>; | 43 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; |
| 44 | using RingAnalogDevices = | ||
| 45 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 46 | using NfcDevices = | ||
| 47 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 42 | using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; | 48 | using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; |
| 43 | 49 | ||
| 44 | using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; | 50 | using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; |
| 45 | using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; | 51 | using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; |
| 46 | using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; | 52 | using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; |
| 47 | using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; | 53 | using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; |
| 54 | using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 48 | using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; | 55 | using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; |
| 49 | using CameraParams = Common::ParamPackage; | 56 | using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>; |
| 50 | using NfcParams = Common::ParamPackage; | 57 | using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>; |
| 58 | using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 51 | using OutputParams = std::array<Common::ParamPackage, output_devices_size>; | 59 | using OutputParams = std::array<Common::ParamPackage, output_devices_size>; |
| 52 | 60 | ||
| 53 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; | 61 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; |
| @@ -58,6 +66,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native | |||
| 58 | using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; | 66 | using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; |
| 59 | using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; | 67 | using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; |
| 60 | using CameraValues = Common::Input::CameraStatus; | 68 | using CameraValues = Common::Input::CameraStatus; |
| 69 | using RingAnalogValue = Common::Input::AnalogStatus; | ||
| 61 | using NfcValues = Common::Input::NfcStatus; | 70 | using NfcValues = Common::Input::NfcStatus; |
| 62 | using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; | 71 | using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; |
| 63 | 72 | ||
| @@ -84,6 +93,10 @@ struct CameraState { | |||
| 84 | std::size_t sample{}; | 93 | std::size_t sample{}; |
| 85 | }; | 94 | }; |
| 86 | 95 | ||
| 96 | struct RingSensorForce { | ||
| 97 | f32 force; | ||
| 98 | }; | ||
| 99 | |||
| 87 | struct NfcState { | 100 | struct NfcState { |
| 88 | Common::Input::NfcState state{}; | 101 | Common::Input::NfcState state{}; |
| 89 | std::vector<u8> data{}; | 102 | std::vector<u8> data{}; |
| @@ -116,6 +129,7 @@ struct ControllerStatus { | |||
| 116 | BatteryValues battery_values{}; | 129 | BatteryValues battery_values{}; |
| 117 | VibrationValues vibration_values{}; | 130 | VibrationValues vibration_values{}; |
| 118 | CameraValues camera_values{}; | 131 | CameraValues camera_values{}; |
| 132 | RingAnalogValue ring_analog_value{}; | ||
| 119 | NfcValues nfc_values{}; | 133 | NfcValues nfc_values{}; |
| 120 | 134 | ||
| 121 | // Data for HID serices | 135 | // Data for HID serices |
| @@ -129,6 +143,7 @@ struct ControllerStatus { | |||
| 129 | ControllerColors colors_state{}; | 143 | ControllerColors colors_state{}; |
| 130 | BatteryLevelState battery_state{}; | 144 | BatteryLevelState battery_state{}; |
| 131 | CameraState camera_state{}; | 145 | CameraState camera_state{}; |
| 146 | RingSensorForce ring_analog_state{}; | ||
| 132 | NfcState nfc_state{}; | 147 | NfcState nfc_state{}; |
| 133 | }; | 148 | }; |
| 134 | 149 | ||
| @@ -141,6 +156,7 @@ enum class ControllerTriggerType { | |||
| 141 | Battery, | 156 | Battery, |
| 142 | Vibration, | 157 | Vibration, |
| 143 | IrSensor, | 158 | IrSensor, |
| 159 | RingController, | ||
| 144 | Nfc, | 160 | Nfc, |
| 145 | Connected, | 161 | Connected, |
| 146 | Disconnected, | 162 | Disconnected, |
| @@ -294,6 +310,9 @@ public: | |||
| 294 | /// Returns the latest camera status from the controller with parameters | 310 | /// Returns the latest camera status from the controller with parameters |
| 295 | CameraValues GetCameraValues() const; | 311 | CameraValues GetCameraValues() const; |
| 296 | 312 | ||
| 313 | /// Returns the latest status of analog input from the ring sensor with parameters | ||
| 314 | RingAnalogValue GetRingSensorValues() const; | ||
| 315 | |||
| 297 | /// Returns the latest status of button input for the hid::HomeButton service | 316 | /// Returns the latest status of button input for the hid::HomeButton service |
| 298 | HomeButtonState GetHomeButtons() const; | 317 | HomeButtonState GetHomeButtons() const; |
| 299 | 318 | ||
| @@ -324,6 +343,9 @@ public: | |||
| 324 | /// Returns the latest camera status from the controller | 343 | /// Returns the latest camera status from the controller |
| 325 | const CameraState& GetCamera() const; | 344 | const CameraState& GetCamera() const; |
| 326 | 345 | ||
| 346 | /// Returns the latest ringcon force sensor value | ||
| 347 | RingSensorForce GetRingSensorForce() const; | ||
| 348 | |||
| 327 | /// Returns the latest ntag status from the controller | 349 | /// Returns the latest ntag status from the controller |
| 328 | const NfcState& GetNfc() const; | 350 | const NfcState& GetNfc() const; |
| 329 | 351 | ||
| @@ -341,10 +363,12 @@ public: | |||
| 341 | 363 | ||
| 342 | /** | 364 | /** |
| 343 | * Sets the desired data to be polled from a controller | 365 | * Sets the desired data to be polled from a controller |
| 366 | * @param device_index index of the controller to set the polling mode | ||
| 344 | * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. | 367 | * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. |
| 345 | * @return true if SetPollingMode was successfull | 368 | * @return driver result from this command |
| 346 | */ | 369 | */ |
| 347 | bool SetPollingMode(Common::Input::PollingMode polling_mode); | 370 | Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index, |
| 371 | Common::Input::PollingMode polling_mode); | ||
| 348 | 372 | ||
| 349 | /** | 373 | /** |
| 350 | * Sets the desired camera format to be polled from a controller | 374 | * Sets the desired camera format to be polled from a controller |
| @@ -353,6 +377,15 @@ public: | |||
| 353 | */ | 377 | */ |
| 354 | bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); | 378 | bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); |
| 355 | 379 | ||
| 380 | // Returns the current mapped ring device | ||
| 381 | Common::ParamPackage GetRingParam() const; | ||
| 382 | |||
| 383 | /** | ||
| 384 | * Updates the current mapped ring device | ||
| 385 | * @param param ParamPackage with ring sensor data to be mapped | ||
| 386 | */ | ||
| 387 | void SetRingParam(Common::ParamPackage param); | ||
| 388 | |||
| 356 | /// Returns true if the device has nfc support | 389 | /// Returns true if the device has nfc support |
| 357 | bool HasNfc() const; | 390 | bool HasNfc() const; |
| 358 | 391 | ||
| @@ -433,9 +466,16 @@ private: | |||
| 433 | void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); | 466 | void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); |
| 434 | 467 | ||
| 435 | /** | 468 | /** |
| 469 | * Updates the color status of the controller | ||
| 470 | * @param callback A CallbackStatus containing the color status | ||
| 471 | * @param index color ID of the to be updated | ||
| 472 | */ | ||
| 473 | void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index); | ||
| 474 | |||
| 475 | /** | ||
| 436 | * Updates the battery status of the controller | 476 | * Updates the battery status of the controller |
| 437 | * @param callback A CallbackStatus containing the battery status | 477 | * @param callback A CallbackStatus containing the battery status |
| 438 | * @param index Button ID of the to be updated | 478 | * @param index battery ID of the to be updated |
| 439 | */ | 479 | */ |
| 440 | void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); | 480 | void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); |
| 441 | 481 | ||
| @@ -446,6 +486,12 @@ private: | |||
| 446 | void SetCamera(const Common::Input::CallbackStatus& callback); | 486 | void SetCamera(const Common::Input::CallbackStatus& callback); |
| 447 | 487 | ||
| 448 | /** | 488 | /** |
| 489 | * Updates the ring analog sensor status of the ring controller | ||
| 490 | * @param callback A CallbackStatus containing the force status | ||
| 491 | */ | ||
| 492 | void SetRingAnalog(const Common::Input::CallbackStatus& callback); | ||
| 493 | |||
| 494 | /** | ||
| 449 | * Updates the nfc status of the controller | 495 | * Updates the nfc status of the controller |
| 450 | * @param callback A CallbackStatus containing the nfc status | 496 | * @param callback A CallbackStatus containing the nfc status |
| 451 | */ | 497 | */ |
| @@ -484,7 +530,9 @@ private: | |||
| 484 | ControllerMotionParams motion_params; | 530 | ControllerMotionParams motion_params; |
| 485 | TriggerParams trigger_params; | 531 | TriggerParams trigger_params; |
| 486 | BatteryParams battery_params; | 532 | BatteryParams battery_params; |
| 533 | ColorParams color_params; | ||
| 487 | CameraParams camera_params; | 534 | CameraParams camera_params; |
| 535 | RingAnalogParams ring_params; | ||
| 488 | NfcParams nfc_params; | 536 | NfcParams nfc_params; |
| 489 | OutputParams output_params; | 537 | OutputParams output_params; |
| 490 | 538 | ||
| @@ -493,7 +541,9 @@ private: | |||
| 493 | ControllerMotionDevices motion_devices; | 541 | ControllerMotionDevices motion_devices; |
| 494 | TriggerDevices trigger_devices; | 542 | TriggerDevices trigger_devices; |
| 495 | BatteryDevices battery_devices; | 543 | BatteryDevices battery_devices; |
| 544 | ColorDevices color_devices; | ||
| 496 | CameraDevices camera_devices; | 545 | CameraDevices camera_devices; |
| 546 | RingAnalogDevices ring_analog_devices; | ||
| 497 | NfcDevices nfc_devices; | 547 | NfcDevices nfc_devices; |
| 498 | OutputDevices output_devices; | 548 | OutputDevices output_devices; |
| 499 | 549 | ||
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp index e421828d2..836f32c0f 100644 --- a/src/core/hid/emulated_devices.cpp +++ b/src/core/hid/emulated_devices.cpp | |||
| @@ -14,7 +14,6 @@ EmulatedDevices::EmulatedDevices() = default; | |||
| 14 | EmulatedDevices::~EmulatedDevices() = default; | 14 | EmulatedDevices::~EmulatedDevices() = default; |
| 15 | 15 | ||
| 16 | void EmulatedDevices::ReloadFromSettings() { | 16 | void EmulatedDevices::ReloadFromSettings() { |
| 17 | ring_params = Common::ParamPackage(Settings::values.ringcon_analogs); | ||
| 18 | ReloadInput(); | 17 | ReloadInput(); |
| 19 | } | 18 | } |
| 20 | 19 | ||
| @@ -66,8 +65,6 @@ void EmulatedDevices::ReloadInput() { | |||
| 66 | key_index++; | 65 | key_index++; |
| 67 | } | 66 | } |
| 68 | 67 | ||
| 69 | ring_analog_device = Common::Input::CreateInputDevice(ring_params); | ||
| 70 | |||
| 71 | for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { | 68 | for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { |
| 72 | if (!mouse_button_devices[index]) { | 69 | if (!mouse_button_devices[index]) { |
| 73 | continue; | 70 | continue; |
| @@ -122,13 +119,6 @@ void EmulatedDevices::ReloadInput() { | |||
| 122 | }, | 119 | }, |
| 123 | }); | 120 | }); |
| 124 | } | 121 | } |
| 125 | |||
| 126 | if (ring_analog_device) { | ||
| 127 | ring_analog_device->SetCallback({ | ||
| 128 | .on_change = | ||
| 129 | [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); }, | ||
| 130 | }); | ||
| 131 | } | ||
| 132 | } | 122 | } |
| 133 | 123 | ||
| 134 | void EmulatedDevices::UnloadInput() { | 124 | void EmulatedDevices::UnloadInput() { |
| @@ -145,7 +135,6 @@ void EmulatedDevices::UnloadInput() { | |||
| 145 | for (auto& button : keyboard_modifier_devices) { | 135 | for (auto& button : keyboard_modifier_devices) { |
| 146 | button.reset(); | 136 | button.reset(); |
| 147 | } | 137 | } |
| 148 | ring_analog_device.reset(); | ||
| 149 | } | 138 | } |
| 150 | 139 | ||
| 151 | void EmulatedDevices::EnableConfiguration() { | 140 | void EmulatedDevices::EnableConfiguration() { |
| @@ -165,7 +154,6 @@ void EmulatedDevices::SaveCurrentConfig() { | |||
| 165 | if (!is_configuring) { | 154 | if (!is_configuring) { |
| 166 | return; | 155 | return; |
| 167 | } | 156 | } |
| 168 | Settings::values.ringcon_analogs = ring_params.Serialize(); | ||
| 169 | } | 157 | } |
| 170 | 158 | ||
| 171 | void EmulatedDevices::RestoreConfig() { | 159 | void EmulatedDevices::RestoreConfig() { |
| @@ -175,15 +163,6 @@ void EmulatedDevices::RestoreConfig() { | |||
| 175 | ReloadFromSettings(); | 163 | ReloadFromSettings(); |
| 176 | } | 164 | } |
| 177 | 165 | ||
| 178 | Common::ParamPackage EmulatedDevices::GetRingParam() const { | ||
| 179 | return ring_params; | ||
| 180 | } | ||
| 181 | |||
| 182 | void EmulatedDevices::SetRingParam(Common::ParamPackage param) { | ||
| 183 | ring_params = std::move(param); | ||
| 184 | ReloadInput(); | ||
| 185 | } | ||
| 186 | |||
| 187 | void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, | 166 | void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, |
| 188 | std::size_t index) { | 167 | std::size_t index) { |
| 189 | if (index >= device_status.keyboard_values.size()) { | 168 | if (index >= device_status.keyboard_values.size()) { |
| @@ -430,23 +409,6 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac | |||
| 430 | TriggerOnChange(DeviceTriggerType::Mouse); | 409 | TriggerOnChange(DeviceTriggerType::Mouse); |
| 431 | } | 410 | } |
| 432 | 411 | ||
| 433 | void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) { | ||
| 434 | std::lock_guard lock{mutex}; | ||
| 435 | const auto force_value = TransformToStick(callback); | ||
| 436 | |||
| 437 | device_status.ring_analog_value = force_value.x; | ||
| 438 | |||
| 439 | if (is_configuring) { | ||
| 440 | device_status.ring_analog_value = {}; | ||
| 441 | TriggerOnChange(DeviceTriggerType::RingController); | ||
| 442 | return; | ||
| 443 | } | ||
| 444 | |||
| 445 | device_status.ring_analog_state.force = force_value.x.value; | ||
| 446 | |||
| 447 | TriggerOnChange(DeviceTriggerType::RingController); | ||
| 448 | } | ||
| 449 | |||
| 450 | KeyboardValues EmulatedDevices::GetKeyboardValues() const { | 412 | KeyboardValues EmulatedDevices::GetKeyboardValues() const { |
| 451 | std::scoped_lock lock{mutex}; | 413 | std::scoped_lock lock{mutex}; |
| 452 | return device_status.keyboard_values; | 414 | return device_status.keyboard_values; |
| @@ -462,10 +424,6 @@ MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { | |||
| 462 | return device_status.mouse_button_values; | 424 | return device_status.mouse_button_values; |
| 463 | } | 425 | } |
| 464 | 426 | ||
| 465 | RingAnalogValue EmulatedDevices::GetRingSensorValues() const { | ||
| 466 | return device_status.ring_analog_value; | ||
| 467 | } | ||
| 468 | |||
| 469 | KeyboardKey EmulatedDevices::GetKeyboard() const { | 427 | KeyboardKey EmulatedDevices::GetKeyboard() const { |
| 470 | std::scoped_lock lock{mutex}; | 428 | std::scoped_lock lock{mutex}; |
| 471 | return device_status.keyboard_state; | 429 | return device_status.keyboard_state; |
| @@ -491,10 +449,6 @@ AnalogStickState EmulatedDevices::GetMouseWheel() const { | |||
| 491 | return device_status.mouse_wheel_state; | 449 | return device_status.mouse_wheel_state; |
| 492 | } | 450 | } |
| 493 | 451 | ||
| 494 | RingSensorForce EmulatedDevices::GetRingSensorForce() const { | ||
| 495 | return device_status.ring_analog_state; | ||
| 496 | } | ||
| 497 | |||
| 498 | void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { | 452 | void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { |
| 499 | std::scoped_lock lock{callback_mutex}; | 453 | std::scoped_lock lock{callback_mutex}; |
| 500 | for (const auto& poller_pair : callback_list) { | 454 | for (const auto& poller_pair : callback_list) { |
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h index 4cdbf9dc6..76f9150df 100644 --- a/src/core/hid/emulated_devices.h +++ b/src/core/hid/emulated_devices.h | |||
| @@ -26,11 +26,9 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice | |||
| 26 | using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, | 26 | using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, |
| 27 | Settings::NativeMouseWheel::NumMouseWheels>; | 27 | Settings::NativeMouseWheel::NumMouseWheels>; |
| 28 | using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; | 28 | using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; |
| 29 | using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>; | ||
| 30 | 29 | ||
| 31 | using MouseButtonParams = | 30 | using MouseButtonParams = |
| 32 | std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; | 31 | std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; |
| 33 | using RingAnalogParams = Common::ParamPackage; | ||
| 34 | 32 | ||
| 35 | using KeyboardValues = | 33 | using KeyboardValues = |
| 36 | std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; | 34 | std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; |
| @@ -41,17 +39,12 @@ using MouseButtonValues = | |||
| 41 | using MouseAnalogValues = | 39 | using MouseAnalogValues = |
| 42 | std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; | 40 | std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; |
| 43 | using MouseStickValue = Common::Input::TouchStatus; | 41 | using MouseStickValue = Common::Input::TouchStatus; |
| 44 | using RingAnalogValue = Common::Input::AnalogStatus; | ||
| 45 | 42 | ||
| 46 | struct MousePosition { | 43 | struct MousePosition { |
| 47 | f32 x; | 44 | f32 x; |
| 48 | f32 y; | 45 | f32 y; |
| 49 | }; | 46 | }; |
| 50 | 47 | ||
| 51 | struct RingSensorForce { | ||
| 52 | f32 force; | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct DeviceStatus { | 48 | struct DeviceStatus { |
| 56 | // Data from input_common | 49 | // Data from input_common |
| 57 | KeyboardValues keyboard_values{}; | 50 | KeyboardValues keyboard_values{}; |
| @@ -59,7 +52,6 @@ struct DeviceStatus { | |||
| 59 | MouseButtonValues mouse_button_values{}; | 52 | MouseButtonValues mouse_button_values{}; |
| 60 | MouseAnalogValues mouse_analog_values{}; | 53 | MouseAnalogValues mouse_analog_values{}; |
| 61 | MouseStickValue mouse_stick_value{}; | 54 | MouseStickValue mouse_stick_value{}; |
| 62 | RingAnalogValue ring_analog_value{}; | ||
| 63 | 55 | ||
| 64 | // Data for HID serices | 56 | // Data for HID serices |
| 65 | KeyboardKey keyboard_state{}; | 57 | KeyboardKey keyboard_state{}; |
| @@ -67,7 +59,6 @@ struct DeviceStatus { | |||
| 67 | MouseButton mouse_button_state{}; | 59 | MouseButton mouse_button_state{}; |
| 68 | MousePosition mouse_position_state{}; | 60 | MousePosition mouse_position_state{}; |
| 69 | AnalogStickState mouse_wheel_state{}; | 61 | AnalogStickState mouse_wheel_state{}; |
| 70 | RingSensorForce ring_analog_state{}; | ||
| 71 | }; | 62 | }; |
| 72 | 63 | ||
| 73 | enum class DeviceTriggerType { | 64 | enum class DeviceTriggerType { |
| @@ -138,9 +129,6 @@ public: | |||
| 138 | /// Returns the latest status of button input from the mouse with parameters | 129 | /// Returns the latest status of button input from the mouse with parameters |
| 139 | MouseButtonValues GetMouseButtonsValues() const; | 130 | MouseButtonValues GetMouseButtonsValues() const; |
| 140 | 131 | ||
| 141 | /// Returns the latest status of analog input from the ring sensor with parameters | ||
| 142 | RingAnalogValue GetRingSensorValues() const; | ||
| 143 | |||
| 144 | /// Returns the latest status of button input from the keyboard | 132 | /// Returns the latest status of button input from the keyboard |
| 145 | KeyboardKey GetKeyboard() const; | 133 | KeyboardKey GetKeyboard() const; |
| 146 | 134 | ||
| @@ -156,9 +144,6 @@ public: | |||
| 156 | /// Returns the latest mouse wheel change | 144 | /// Returns the latest mouse wheel change |
| 157 | AnalogStickState GetMouseWheel() const; | 145 | AnalogStickState GetMouseWheel() const; |
| 158 | 146 | ||
| 159 | /// Returns the latest ringcon force sensor value | ||
| 160 | RingSensorForce GetRingSensorForce() const; | ||
| 161 | |||
| 162 | /** | 147 | /** |
| 163 | * Adds a callback to the list of events | 148 | * Adds a callback to the list of events |
| 164 | * @param update_callback InterfaceUpdateCallback that will be triggered | 149 | * @param update_callback InterfaceUpdateCallback that will be triggered |
| @@ -224,14 +209,11 @@ private: | |||
| 224 | 209 | ||
| 225 | bool is_configuring{false}; | 210 | bool is_configuring{false}; |
| 226 | 211 | ||
| 227 | RingAnalogParams ring_params; | ||
| 228 | |||
| 229 | KeyboardDevices keyboard_devices; | 212 | KeyboardDevices keyboard_devices; |
| 230 | KeyboardModifierDevices keyboard_modifier_devices; | 213 | KeyboardModifierDevices keyboard_modifier_devices; |
| 231 | MouseButtonDevices mouse_button_devices; | 214 | MouseButtonDevices mouse_button_devices; |
| 232 | MouseAnalogDevices mouse_analog_devices; | 215 | MouseAnalogDevices mouse_analog_devices; |
| 233 | MouseStickDevice mouse_stick_device; | 216 | MouseStickDevice mouse_stick_device; |
| 234 | RingAnalogDevice ring_analog_device; | ||
| 235 | 217 | ||
| 236 | mutable std::mutex mutex; | 218 | mutable std::mutex mutex; |
| 237 | mutable std::mutex callback_mutex; | 219 | mutable std::mutex callback_mutex; |
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 502692875..3f7b8c090 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp | |||
| @@ -304,6 +304,18 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal | |||
| 304 | return nfc; | 304 | return nfc; |
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) { | ||
| 308 | switch (callback.type) { | ||
| 309 | case Common::Input::InputType::Color: | ||
| 310 | return callback.color_status; | ||
| 311 | break; | ||
| 312 | default: | ||
| 313 | LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type); | ||
| 314 | return {}; | ||
| 315 | break; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 307 | void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { | 319 | void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { |
| 308 | const auto& properties = analog.properties; | 320 | const auto& properties = analog.properties; |
| 309 | float& raw_value = analog.raw_value; | 321 | float& raw_value = analog.raw_value; |
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h index b7eb6e660..c51c03e57 100644 --- a/src/core/hid/input_converter.h +++ b/src/core/hid/input_converter.h | |||
| @@ -88,11 +88,19 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu | |||
| 88 | * Converts raw input data into a valid nfc status. | 88 | * Converts raw input data into a valid nfc status. |
| 89 | * | 89 | * |
| 90 | * @param callback Supported callbacks: Nfc. | 90 | * @param callback Supported callbacks: Nfc. |
| 91 | * @return A valid CameraObject object. | 91 | * @return A valid data tag vector. |
| 92 | */ | 92 | */ |
| 93 | Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); | 93 | Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); |
| 94 | 94 | ||
| 95 | /** | 95 | /** |
| 96 | * Converts raw input data into a valid color status. | ||
| 97 | * | ||
| 98 | * @param callback Supported callbacks: Color. | ||
| 99 | * @return A valid Color object. | ||
| 100 | */ | ||
| 101 | Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback); | ||
| 102 | |||
| 103 | /** | ||
| 96 | * Converts raw analog data into a valid analog value | 104 | * Converts raw analog data into a valid analog value |
| 97 | * @param analog An analog object containing raw data and properties | 105 | * @param analog An analog object containing raw data and properties |
| 98 | * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. | 106 | * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. |
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index d9da1e600..884eba001 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp | |||
| @@ -74,7 +74,7 @@ Result KCodeMemory::Map(VAddr address, size_t size) { | |||
| 74 | R_UNLESS(!m_is_mapped, ResultInvalidState); | 74 | R_UNLESS(!m_is_mapped, ResultInvalidState); |
| 75 | 75 | ||
| 76 | // Map the memory. | 76 | // Map the memory. |
| 77 | R_TRY(kernel.CurrentProcess()->PageTable().MapPages( | 77 | R_TRY(kernel.CurrentProcess()->PageTable().MapPageGroup( |
| 78 | address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); | 78 | address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); |
| 79 | 79 | ||
| 80 | // Mark ourselves as mapped. | 80 | // Mark ourselves as mapped. |
| @@ -91,8 +91,8 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) { | |||
| 91 | KScopedLightLock lk(m_lock); | 91 | KScopedLightLock lk(m_lock); |
| 92 | 92 | ||
| 93 | // Unmap the memory. | 93 | // Unmap the memory. |
| 94 | R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, *m_page_group, | 94 | R_TRY(kernel.CurrentProcess()->PageTable().UnmapPageGroup(address, *m_page_group, |
| 95 | KMemoryState::CodeOut)); | 95 | KMemoryState::CodeOut)); |
| 96 | 96 | ||
| 97 | // Mark ourselves as unmapped. | 97 | // Mark ourselves as unmapped. |
| 98 | m_is_mapped = false; | 98 | m_is_mapped = false; |
| @@ -125,8 +125,8 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission | |||
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | // Map the memory. | 127 | // Map the memory. |
| 128 | R_TRY( | 128 | R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode, |
| 129 | m_owner->PageTable().MapPages(address, *m_page_group, KMemoryState::GeneratedCode, k_perm)); | 129 | k_perm)); |
| 130 | 130 | ||
| 131 | // Mark ourselves as mapped. | 131 | // Mark ourselves as mapped. |
| 132 | m_is_owner_mapped = true; | 132 | m_is_owner_mapped = true; |
| @@ -142,7 +142,7 @@ Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { | |||
| 142 | KScopedLightLock lk(m_lock); | 142 | KScopedLightLock lk(m_lock); |
| 143 | 143 | ||
| 144 | // Unmap the memory. | 144 | // Unmap the memory. |
| 145 | R_TRY(m_owner->PageTable().UnmapPages(address, *m_page_group, KMemoryState::GeneratedCode)); | 145 | R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode)); |
| 146 | 146 | ||
| 147 | // Mark ourselves as unmapped. | 147 | // Mark ourselves as unmapped. |
| 148 | m_is_owner_mapped = false; | 148 | m_is_owner_mapped = false; |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 9c7ac22dc..2e13d5d0d 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -435,6 +435,9 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si | |||
| 435 | KPageGroup pg{m_kernel, m_block_info_manager}; | 435 | KPageGroup pg{m_kernel, m_block_info_manager}; |
| 436 | AddRegionToPages(src_address, num_pages, pg); | 436 | AddRegionToPages(src_address, num_pages, pg); |
| 437 | 437 | ||
| 438 | // We're going to perform an update, so create a helper. | ||
| 439 | KScopedPageTableUpdater updater(this); | ||
| 440 | |||
| 438 | // Reprotect the source as kernel-read/not mapped. | 441 | // Reprotect the source as kernel-read/not mapped. |
| 439 | const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | | 442 | const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | |
| 440 | KMemoryPermission::NotMapped); | 443 | KMemoryPermission::NotMapped); |
| @@ -447,7 +450,10 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si | |||
| 447 | }); | 450 | }); |
| 448 | 451 | ||
| 449 | // Map the alias pages. | 452 | // Map the alias pages. |
| 450 | R_TRY(MapPages(dst_address, pg, new_perm)); | 453 | const KPageProperties dst_properties = {new_perm, false, false, |
| 454 | DisableMergeAttribute::DisableHead}; | ||
| 455 | R_TRY( | ||
| 456 | this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false)); | ||
| 451 | 457 | ||
| 452 | // We successfully mapped the alias pages, so we don't need to unprotect the src pages on | 458 | // We successfully mapped the alias pages, so we don't need to unprotect the src pages on |
| 453 | // failure. | 459 | // failure. |
| @@ -1881,7 +1887,8 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { | |||
| 1881 | R_SUCCEED(); | 1887 | R_SUCCEED(); |
| 1882 | } | 1888 | } |
| 1883 | 1889 | ||
| 1884 | Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { | 1890 | Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address, |
| 1891 | size_t size) { | ||
| 1885 | // Lock the table. | 1892 | // Lock the table. |
| 1886 | KScopedLightLock lk(m_general_lock); | 1893 | KScopedLightLock lk(m_general_lock); |
| 1887 | 1894 | ||
| @@ -1902,53 +1909,73 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) | |||
| 1902 | KMemoryAttribute::None)); | 1909 | KMemoryAttribute::None)); |
| 1903 | 1910 | ||
| 1904 | // Create an update allocator for the source. | 1911 | // Create an update allocator for the source. |
| 1905 | Result src_allocator_result{ResultSuccess}; | 1912 | Result src_allocator_result; |
| 1906 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | 1913 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), |
| 1907 | m_memory_block_slab_manager, | 1914 | m_memory_block_slab_manager, |
| 1908 | num_src_allocator_blocks); | 1915 | num_src_allocator_blocks); |
| 1909 | R_TRY(src_allocator_result); | 1916 | R_TRY(src_allocator_result); |
| 1910 | 1917 | ||
| 1911 | // Create an update allocator for the destination. | 1918 | // Create an update allocator for the destination. |
| 1912 | Result dst_allocator_result{ResultSuccess}; | 1919 | Result dst_allocator_result; |
| 1913 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | 1920 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), |
| 1914 | m_memory_block_slab_manager, | 1921 | m_memory_block_slab_manager, |
| 1915 | num_dst_allocator_blocks); | 1922 | num_dst_allocator_blocks); |
| 1916 | R_TRY(dst_allocator_result); | 1923 | R_TRY(dst_allocator_result); |
| 1917 | 1924 | ||
| 1918 | // Map the memory. | 1925 | // Map the memory. |
| 1919 | KPageGroup page_linked_list{m_kernel, m_block_info_manager}; | ||
| 1920 | const size_t num_pages{size / PageSize}; | ||
| 1921 | const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( | ||
| 1922 | KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); | ||
| 1923 | const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; | ||
| 1924 | |||
| 1925 | AddRegionToPages(src_address, num_pages, page_linked_list); | ||
| 1926 | { | 1926 | { |
| 1927 | // Determine the number of pages being operated on. | ||
| 1928 | const size_t num_pages = size / PageSize; | ||
| 1929 | |||
| 1930 | // Create page groups for the memory being unmapped. | ||
| 1931 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 1932 | |||
| 1933 | // Create the page group representing the source. | ||
| 1934 | R_TRY(this->MakePageGroup(pg, src_address, num_pages)); | ||
| 1935 | |||
| 1936 | // We're going to perform an update, so create a helper. | ||
| 1937 | KScopedPageTableUpdater updater(this); | ||
| 1938 | |||
| 1927 | // Reprotect the source as kernel-read/not mapped. | 1939 | // Reprotect the source as kernel-read/not mapped. |
| 1928 | auto block_guard = detail::ScopeExit([&] { | 1940 | const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( |
| 1929 | Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, | 1941 | KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); |
| 1930 | OperationType::ChangePermissions); | 1942 | const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; |
| 1931 | }); | 1943 | const KPageProperties src_properties = {new_src_perm, false, false, |
| 1932 | R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); | 1944 | DisableMergeAttribute::DisableHeadBodyTail}; |
| 1933 | R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); | 1945 | R_TRY(this->Operate(src_address, num_pages, src_properties.perm, |
| 1946 | OperationType::ChangePermissions)); | ||
| 1934 | 1947 | ||
| 1935 | block_guard.Cancel(); | 1948 | // Ensure that we unprotect the source pages on failure. |
| 1936 | } | 1949 | ON_RESULT_FAILURE { |
| 1950 | const KPageProperties unprotect_properties = { | ||
| 1951 | KMemoryPermission::UserReadWrite, false, false, | ||
| 1952 | DisableMergeAttribute::EnableHeadBodyTail}; | ||
| 1953 | ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm, | ||
| 1954 | OperationType::ChangePermissions) == ResultSuccess); | ||
| 1955 | }; | ||
| 1937 | 1956 | ||
| 1938 | // Apply the memory block updates. | 1957 | // Map the alias pages. |
| 1939 | m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, | 1958 | const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false, |
| 1940 | new_src_perm, new_src_attr, | 1959 | DisableMergeAttribute::DisableHead}; |
| 1941 | KMemoryBlockDisableMergeAttribute::Locked, | 1960 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties, |
| 1942 | KMemoryBlockDisableMergeAttribute::None); | 1961 | false)); |
| 1943 | m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, | 1962 | |
| 1944 | KMemoryState::Stack, KMemoryPermission::UserReadWrite, | 1963 | // Apply the memory block updates. |
| 1945 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | 1964 | m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, |
| 1946 | KMemoryBlockDisableMergeAttribute::None); | 1965 | src_state, new_src_perm, new_src_attr, |
| 1966 | KMemoryBlockDisableMergeAttribute::Locked, | ||
| 1967 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1968 | m_memory_block_manager.Update( | ||
| 1969 | std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack, | ||
| 1970 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 1971 | KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); | ||
| 1972 | } | ||
| 1947 | 1973 | ||
| 1948 | R_SUCCEED(); | 1974 | R_SUCCEED(); |
| 1949 | } | 1975 | } |
| 1950 | 1976 | ||
| 1951 | Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { | 1977 | Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, |
| 1978 | size_t size) { | ||
| 1952 | // Lock the table. | 1979 | // Lock the table. |
| 1953 | KScopedLightLock lk(m_general_lock); | 1980 | KScopedLightLock lk(m_general_lock); |
| 1954 | 1981 | ||
| @@ -1970,108 +1997,208 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size | |||
| 1970 | KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); | 1997 | KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); |
| 1971 | 1998 | ||
| 1972 | // Create an update allocator for the source. | 1999 | // Create an update allocator for the source. |
| 1973 | Result src_allocator_result{ResultSuccess}; | 2000 | Result src_allocator_result; |
| 1974 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | 2001 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), |
| 1975 | m_memory_block_slab_manager, | 2002 | m_memory_block_slab_manager, |
| 1976 | num_src_allocator_blocks); | 2003 | num_src_allocator_blocks); |
| 1977 | R_TRY(src_allocator_result); | 2004 | R_TRY(src_allocator_result); |
| 1978 | 2005 | ||
| 1979 | // Create an update allocator for the destination. | 2006 | // Create an update allocator for the destination. |
| 1980 | Result dst_allocator_result{ResultSuccess}; | 2007 | Result dst_allocator_result; |
| 1981 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | 2008 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), |
| 1982 | m_memory_block_slab_manager, | 2009 | m_memory_block_slab_manager, |
| 1983 | num_dst_allocator_blocks); | 2010 | num_dst_allocator_blocks); |
| 1984 | R_TRY(dst_allocator_result); | 2011 | R_TRY(dst_allocator_result); |
| 1985 | 2012 | ||
| 1986 | KPageGroup src_pages{m_kernel, m_block_info_manager}; | 2013 | // Unmap the memory. |
| 1987 | KPageGroup dst_pages{m_kernel, m_block_info_manager}; | 2014 | { |
| 1988 | const size_t num_pages{size / PageSize}; | 2015 | // Determine the number of pages being operated on. |
| 2016 | const size_t num_pages = size / PageSize; | ||
| 1989 | 2017 | ||
| 1990 | AddRegionToPages(src_address, num_pages, src_pages); | 2018 | // Create page groups for the memory being unmapped. |
| 1991 | AddRegionToPages(dst_address, num_pages, dst_pages); | 2019 | KPageGroup pg{m_kernel, m_block_info_manager}; |
| 1992 | 2020 | ||
| 1993 | R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion); | 2021 | // Create the page group representing the destination. |
| 2022 | R_TRY(this->MakePageGroup(pg, dst_address, num_pages)); | ||
| 1994 | 2023 | ||
| 1995 | { | 2024 | // Ensure the page group is the valid for the source. |
| 1996 | auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); | 2025 | R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion); |
| 1997 | 2026 | ||
| 1998 | R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); | 2027 | // We're going to perform an update, so create a helper. |
| 1999 | R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, | 2028 | KScopedPageTableUpdater updater(this); |
| 2000 | OperationType::ChangePermissions)); | ||
| 2001 | 2029 | ||
| 2002 | block_guard.Cancel(); | 2030 | // Unmap the aliased copy of the pages. |
| 2003 | } | 2031 | const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false, |
| 2032 | DisableMergeAttribute::None}; | ||
| 2033 | R_TRY( | ||
| 2034 | this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap)); | ||
| 2035 | |||
| 2036 | // Ensure that we re-map the aliased pages on failure. | ||
| 2037 | ON_RESULT_FAILURE { | ||
| 2038 | this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg); | ||
| 2039 | }; | ||
| 2004 | 2040 | ||
| 2005 | // Apply the memory block updates. | 2041 | // Try to set the permissions for the source pages back to what they should be. |
| 2006 | m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, | 2042 | const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false, |
| 2007 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | 2043 | DisableMergeAttribute::EnableAndMergeHeadBodyTail}; |
| 2008 | KMemoryBlockDisableMergeAttribute::None, | 2044 | R_TRY(this->Operate(src_address, num_pages, src_properties.perm, |
| 2009 | KMemoryBlockDisableMergeAttribute::Locked); | 2045 | OperationType::ChangePermissions)); |
| 2010 | m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, | 2046 | |
| 2011 | KMemoryState::None, KMemoryPermission::None, | 2047 | // Apply the memory block updates. |
| 2012 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | 2048 | m_memory_block_manager.Update( |
| 2013 | KMemoryBlockDisableMergeAttribute::Normal); | 2049 | std::addressof(src_allocator), src_address, num_pages, src_state, |
| 2050 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 2051 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); | ||
| 2052 | m_memory_block_manager.Update( | ||
| 2053 | std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, | ||
| 2054 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2055 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2056 | } | ||
| 2014 | 2057 | ||
| 2015 | R_SUCCEED(); | 2058 | R_SUCCEED(); |
| 2016 | } | 2059 | } |
| 2017 | 2060 | ||
| 2018 | Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, | 2061 | Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, |
| 2019 | KMemoryPermission perm) { | 2062 | size_t num_pages, KMemoryPermission perm) { |
| 2020 | ASSERT(this->IsLockedByCurrentThread()); | 2063 | ASSERT(this->IsLockedByCurrentThread()); |
| 2021 | 2064 | ||
| 2022 | VAddr cur_addr{addr}; | 2065 | // Create a page group to hold the pages we allocate. |
| 2066 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 2023 | 2067 | ||
| 2024 | for (const auto& node : page_linked_list) { | 2068 | // Allocate the pages. |
| 2025 | if (const auto result{ | 2069 | R_TRY( |
| 2026 | Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; | 2070 | m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); |
| 2027 | result.IsError()) { | ||
| 2028 | const size_t num_pages{(addr - cur_addr) / PageSize}; | ||
| 2029 | 2071 | ||
| 2030 | ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) | 2072 | // Ensure that the page group is closed when we're done working with it. |
| 2031 | .IsSuccess()); | 2073 | SCOPE_EXIT({ pg.Close(); }); |
| 2032 | 2074 | ||
| 2033 | R_RETURN(result); | 2075 | // Clear all pages. |
| 2076 | for (const auto& it : pg) { | ||
| 2077 | std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, | ||
| 2078 | it.GetSize()); | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | // Map the pages. | ||
| 2082 | R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup)); | ||
| 2083 | } | ||
| 2084 | |||
| 2085 | Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 2086 | const KPageGroup& pg, const KPageProperties properties, | ||
| 2087 | bool reuse_ll) { | ||
| 2088 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 2089 | |||
| 2090 | // Note the current address, so that we can iterate. | ||
| 2091 | const KProcessAddress start_address = address; | ||
| 2092 | KProcessAddress cur_address = address; | ||
| 2093 | |||
| 2094 | // Ensure that we clean up on failure. | ||
| 2095 | ON_RESULT_FAILURE { | ||
| 2096 | ASSERT(!reuse_ll); | ||
| 2097 | if (cur_address != start_address) { | ||
| 2098 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 2099 | DisableMergeAttribute::None}; | ||
| 2100 | ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize, | ||
| 2101 | unmap_properties.perm, OperationType::Unmap) == ResultSuccess); | ||
| 2034 | } | 2102 | } |
| 2103 | }; | ||
| 2035 | 2104 | ||
| 2036 | cur_addr += node.GetNumPages() * PageSize; | 2105 | // Iterate, mapping all pages in the group. |
| 2106 | for (const auto& block : pg) { | ||
| 2107 | // Map and advance. | ||
| 2108 | const KPageProperties cur_properties = | ||
| 2109 | (cur_address == start_address) | ||
| 2110 | ? properties | ||
| 2111 | : KPageProperties{properties.perm, properties.io, properties.uncached, | ||
| 2112 | DisableMergeAttribute::None}; | ||
| 2113 | this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map, | ||
| 2114 | block.GetAddress()); | ||
| 2115 | cur_address += block.GetSize(); | ||
| 2037 | } | 2116 | } |
| 2038 | 2117 | ||
| 2118 | // We succeeded! | ||
| 2039 | R_SUCCEED(); | 2119 | R_SUCCEED(); |
| 2040 | } | 2120 | } |
| 2041 | 2121 | ||
| 2042 | Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, | 2122 | void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, |
| 2043 | KMemoryPermission perm) { | 2123 | const KPageGroup& pg) { |
| 2044 | // Check that the map is in range. | 2124 | ASSERT(this->IsLockedByCurrentThread()); |
| 2045 | const size_t num_pages{page_linked_list.GetNumPages()}; | ||
| 2046 | const size_t size{num_pages * PageSize}; | ||
| 2047 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); | ||
| 2048 | 2125 | ||
| 2049 | // Lock the table. | 2126 | // Note the current address, so that we can iterate. |
| 2050 | KScopedLightLock lk(m_general_lock); | 2127 | const KProcessAddress start_address = address; |
| 2128 | const KProcessAddress last_address = start_address + size - 1; | ||
| 2129 | const KProcessAddress end_address = last_address + 1; | ||
| 2051 | 2130 | ||
| 2052 | // Check the memory state. | 2131 | // Iterate over the memory. |
| 2053 | R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, | 2132 | auto pg_it = pg.begin(); |
| 2054 | KMemoryPermission::None, KMemoryPermission::None, | 2133 | ASSERT(pg_it != pg.end()); |
| 2055 | KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 2056 | 2134 | ||
| 2057 | // Create an update allocator. | 2135 | KPhysicalAddress pg_phys_addr = pg_it->GetAddress(); |
| 2058 | Result allocator_result{ResultSuccess}; | 2136 | size_t pg_pages = pg_it->GetNumPages(); |
| 2059 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2060 | m_memory_block_slab_manager); | ||
| 2061 | 2137 | ||
| 2062 | // Map the pages. | 2138 | auto it = m_memory_block_manager.FindIterator(start_address); |
| 2063 | R_TRY(MapPages(address, page_linked_list, perm)); | 2139 | while (true) { |
| 2140 | // Check that the iterator is valid. | ||
| 2141 | ASSERT(it != m_memory_block_manager.end()); | ||
| 2064 | 2142 | ||
| 2065 | // Update the blocks. | 2143 | // Get the memory info. |
| 2066 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, | 2144 | const KMemoryInfo info = it->GetMemoryInfo(); |
| 2067 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2068 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2069 | 2145 | ||
| 2070 | R_SUCCEED(); | 2146 | // Determine the range to map. |
| 2147 | KProcessAddress map_address = std::max<VAddr>(info.GetAddress(), start_address); | ||
| 2148 | const KProcessAddress map_end_address = std::min<VAddr>(info.GetEndAddress(), end_address); | ||
| 2149 | ASSERT(map_end_address != map_address); | ||
| 2150 | |||
| 2151 | // Determine if we should disable head merge. | ||
| 2152 | const bool disable_head_merge = | ||
| 2153 | info.GetAddress() >= start_address && | ||
| 2154 | True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2155 | const KPageProperties map_properties = { | ||
| 2156 | info.GetPermission(), false, false, | ||
| 2157 | disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None}; | ||
| 2158 | |||
| 2159 | // While we have pages to map, map them. | ||
| 2160 | size_t map_pages = (map_end_address - map_address) / PageSize; | ||
| 2161 | while (map_pages > 0) { | ||
| 2162 | // Check if we're at the end of the physical block. | ||
| 2163 | if (pg_pages == 0) { | ||
| 2164 | // Ensure there are more pages to map. | ||
| 2165 | ASSERT(pg_it != pg.end()); | ||
| 2166 | |||
| 2167 | // Advance our physical block. | ||
| 2168 | ++pg_it; | ||
| 2169 | pg_phys_addr = pg_it->GetAddress(); | ||
| 2170 | pg_pages = pg_it->GetNumPages(); | ||
| 2171 | } | ||
| 2172 | |||
| 2173 | // Map whatever we can. | ||
| 2174 | const size_t cur_pages = std::min(pg_pages, map_pages); | ||
| 2175 | ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map, | ||
| 2176 | pg_phys_addr) == ResultSuccess); | ||
| 2177 | |||
| 2178 | // Advance. | ||
| 2179 | map_address += cur_pages * PageSize; | ||
| 2180 | map_pages -= cur_pages; | ||
| 2181 | |||
| 2182 | pg_phys_addr += cur_pages * PageSize; | ||
| 2183 | pg_pages -= cur_pages; | ||
| 2184 | } | ||
| 2185 | |||
| 2186 | // Check if we're done. | ||
| 2187 | if (last_address <= info.GetLastAddress()) { | ||
| 2188 | break; | ||
| 2189 | } | ||
| 2190 | |||
| 2191 | // Advance. | ||
| 2192 | ++it; | ||
| 2193 | } | ||
| 2194 | |||
| 2195 | // Check that we re-mapped precisely the page group. | ||
| 2196 | ASSERT((++pg_it) == pg.end()); | ||
| 2071 | } | 2197 | } |
| 2072 | 2198 | ||
| 2073 | Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, | 2199 | Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, |
| 2074 | bool is_pa_valid, VAddr region_start, size_t region_num_pages, | 2200 | KPhysicalAddress phys_addr, bool is_pa_valid, |
| 2201 | KProcessAddress region_start, size_t region_num_pages, | ||
| 2075 | KMemoryState state, KMemoryPermission perm) { | 2202 | KMemoryState state, KMemoryPermission perm) { |
| 2076 | ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); | 2203 | ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); |
| 2077 | 2204 | ||
| @@ -2084,26 +2211,30 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, | |||
| 2084 | KScopedLightLock lk(m_general_lock); | 2211 | KScopedLightLock lk(m_general_lock); |
| 2085 | 2212 | ||
| 2086 | // Find a random address to map at. | 2213 | // Find a random address to map at. |
| 2087 | VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, | 2214 | KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, |
| 2088 | this->GetNumGuardPages()); | 2215 | 0, this->GetNumGuardPages()); |
| 2089 | R_UNLESS(addr != 0, ResultOutOfMemory); | 2216 | R_UNLESS(addr != 0, ResultOutOfMemory); |
| 2090 | ASSERT(Common::IsAligned(addr, alignment)); | 2217 | ASSERT(Common::IsAligned(addr, alignment)); |
| 2091 | ASSERT(this->CanContain(addr, num_pages * PageSize, state)); | 2218 | ASSERT(this->CanContain(addr, num_pages * PageSize, state)); |
| 2092 | ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, | 2219 | ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, |
| 2093 | KMemoryPermission::None, KMemoryPermission::None, | 2220 | KMemoryPermission::None, KMemoryPermission::None, |
| 2094 | KMemoryAttribute::None, KMemoryAttribute::None) | 2221 | KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess); |
| 2095 | .IsSuccess()); | ||
| 2096 | 2222 | ||
| 2097 | // Create an update allocator. | 2223 | // Create an update allocator. |
| 2098 | Result allocator_result{ResultSuccess}; | 2224 | Result allocator_result; |
| 2099 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | 2225 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), |
| 2100 | m_memory_block_slab_manager); | 2226 | m_memory_block_slab_manager); |
| 2227 | R_TRY(allocator_result); | ||
| 2228 | |||
| 2229 | // We're going to perform an update, so create a helper. | ||
| 2230 | KScopedPageTableUpdater updater(this); | ||
| 2101 | 2231 | ||
| 2102 | // Perform mapping operation. | 2232 | // Perform mapping operation. |
| 2103 | if (is_pa_valid) { | 2233 | if (is_pa_valid) { |
| 2104 | R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); | 2234 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; |
| 2235 | R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr)); | ||
| 2105 | } else { | 2236 | } else { |
| 2106 | UNIMPLEMENTED(); | 2237 | R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm)); |
| 2107 | } | 2238 | } |
| 2108 | 2239 | ||
| 2109 | // Update the blocks. | 2240 | // Update the blocks. |
| @@ -2116,28 +2247,45 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, | |||
| 2116 | R_SUCCEED(); | 2247 | R_SUCCEED(); |
| 2117 | } | 2248 | } |
| 2118 | 2249 | ||
| 2119 | Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { | 2250 | Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, |
| 2120 | ASSERT(this->IsLockedByCurrentThread()); | 2251 | KMemoryPermission perm) { |
| 2252 | // Check that the map is in range. | ||
| 2253 | const size_t size = num_pages * PageSize; | ||
| 2254 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); | ||
| 2121 | 2255 | ||
| 2122 | VAddr cur_addr{addr}; | 2256 | // Lock the table. |
| 2257 | KScopedLightLock lk(m_general_lock); | ||
| 2123 | 2258 | ||
| 2124 | for (const auto& node : page_linked_list) { | 2259 | // Check the memory state. |
| 2125 | if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, | 2260 | size_t num_allocator_blocks; |
| 2126 | OperationType::Unmap)}; | 2261 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, |
| 2127 | result.IsError()) { | 2262 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, |
| 2128 | R_RETURN(result); | 2263 | KMemoryPermission::None, KMemoryAttribute::None, |
| 2129 | } | 2264 | KMemoryAttribute::None)); |
| 2130 | 2265 | ||
| 2131 | cur_addr += node.GetNumPages() * PageSize; | 2266 | // Create an update allocator. |
| 2132 | } | 2267 | Result allocator_result; |
| 2268 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2269 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2270 | R_TRY(allocator_result); | ||
| 2271 | |||
| 2272 | // We're going to perform an update, so create a helper. | ||
| 2273 | KScopedPageTableUpdater updater(this); | ||
| 2274 | |||
| 2275 | // Map the pages. | ||
| 2276 | R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm)); | ||
| 2277 | |||
| 2278 | // Update the blocks. | ||
| 2279 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, | ||
| 2280 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2281 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2133 | 2282 | ||
| 2134 | R_SUCCEED(); | 2283 | R_SUCCEED(); |
| 2135 | } | 2284 | } |
| 2136 | 2285 | ||
| 2137 | Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { | 2286 | Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) { |
| 2138 | // Check that the unmap is in range. | 2287 | // Check that the unmap is in range. |
| 2139 | const size_t num_pages{page_linked_list.GetNumPages()}; | 2288 | const size_t size = num_pages * PageSize; |
| 2140 | const size_t size{num_pages * PageSize}; | ||
| 2141 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | 2289 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); |
| 2142 | 2290 | ||
| 2143 | // Lock the table. | 2291 | // Lock the table. |
| @@ -2151,13 +2299,18 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo | |||
| 2151 | KMemoryAttribute::None)); | 2299 | KMemoryAttribute::None)); |
| 2152 | 2300 | ||
| 2153 | // Create an update allocator. | 2301 | // Create an update allocator. |
| 2154 | Result allocator_result{ResultSuccess}; | 2302 | Result allocator_result; |
| 2155 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | 2303 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), |
| 2156 | m_memory_block_slab_manager, num_allocator_blocks); | 2304 | m_memory_block_slab_manager, num_allocator_blocks); |
| 2157 | R_TRY(allocator_result); | 2305 | R_TRY(allocator_result); |
| 2158 | 2306 | ||
| 2307 | // We're going to perform an update, so create a helper. | ||
| 2308 | KScopedPageTableUpdater updater(this); | ||
| 2309 | |||
| 2159 | // Perform the unmap. | 2310 | // Perform the unmap. |
| 2160 | R_TRY(UnmapPages(address, page_linked_list)); | 2311 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, |
| 2312 | DisableMergeAttribute::None}; | ||
| 2313 | R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap)); | ||
| 2161 | 2314 | ||
| 2162 | // Update the blocks. | 2315 | // Update the blocks. |
| 2163 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, | 2316 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, |
| @@ -2168,29 +2321,130 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo | |||
| 2168 | R_SUCCEED(); | 2321 | R_SUCCEED(); |
| 2169 | } | 2322 | } |
| 2170 | 2323 | ||
| 2171 | Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { | 2324 | Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, |
| 2172 | // Check that the unmap is in range. | 2325 | KProcessAddress region_start, size_t region_num_pages, |
| 2326 | KMemoryState state, KMemoryPermission perm) { | ||
| 2327 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2328 | |||
| 2329 | // Ensure this is a valid map request. | ||
| 2330 | const size_t num_pages = pg.GetNumPages(); | ||
| 2331 | R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), | ||
| 2332 | ResultInvalidCurrentMemory); | ||
| 2333 | R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); | ||
| 2334 | |||
| 2335 | // Lock the table. | ||
| 2336 | KScopedLightLock lk(m_general_lock); | ||
| 2337 | |||
| 2338 | // Find a random address to map at. | ||
| 2339 | KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, | ||
| 2340 | 0, this->GetNumGuardPages()); | ||
| 2341 | R_UNLESS(addr != 0, ResultOutOfMemory); | ||
| 2342 | ASSERT(this->CanContain(addr, num_pages * PageSize, state)); | ||
| 2343 | ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, | ||
| 2344 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2345 | KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess); | ||
| 2346 | |||
| 2347 | // Create an update allocator. | ||
| 2348 | Result allocator_result; | ||
| 2349 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2350 | m_memory_block_slab_manager); | ||
| 2351 | R_TRY(allocator_result); | ||
| 2352 | |||
| 2353 | // We're going to perform an update, so create a helper. | ||
| 2354 | KScopedPageTableUpdater updater(this); | ||
| 2355 | |||
| 2356 | // Perform mapping operation. | ||
| 2357 | const KPageProperties properties = {perm, state == KMemoryState::Io, false, | ||
| 2358 | DisableMergeAttribute::DisableHead}; | ||
| 2359 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); | ||
| 2360 | |||
| 2361 | // Update the blocks. | ||
| 2362 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 2363 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2364 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2365 | |||
| 2366 | // We successfully mapped the pages. | ||
| 2367 | *out_addr = addr; | ||
| 2368 | R_SUCCEED(); | ||
| 2369 | } | ||
| 2370 | |||
| 2371 | Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state, | ||
| 2372 | KMemoryPermission perm) { | ||
| 2373 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2374 | |||
| 2375 | // Ensure this is a valid map request. | ||
| 2376 | const size_t num_pages = pg.GetNumPages(); | ||
| 2173 | const size_t size = num_pages * PageSize; | 2377 | const size_t size = num_pages * PageSize; |
| 2174 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | 2378 | R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); |
| 2175 | 2379 | ||
| 2176 | // Lock the table. | 2380 | // Lock the table. |
| 2177 | KScopedLightLock lk(m_general_lock); | 2381 | KScopedLightLock lk(m_general_lock); |
| 2178 | 2382 | ||
| 2179 | // Check the memory state. | 2383 | // Check if state allows us to map. |
| 2180 | size_t num_allocator_blocks{}; | 2384 | size_t num_allocator_blocks; |
| 2385 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size, | ||
| 2386 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 2387 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2388 | KMemoryAttribute::None)); | ||
| 2389 | |||
| 2390 | // Create an update allocator. | ||
| 2391 | Result allocator_result; | ||
| 2392 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2393 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2394 | R_TRY(allocator_result); | ||
| 2395 | |||
| 2396 | // We're going to perform an update, so create a helper. | ||
| 2397 | KScopedPageTableUpdater updater(this); | ||
| 2398 | |||
| 2399 | // Perform mapping operation. | ||
| 2400 | const KPageProperties properties = {perm, state == KMemoryState::Io, false, | ||
| 2401 | DisableMergeAttribute::DisableHead}; | ||
| 2402 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); | ||
| 2403 | |||
| 2404 | // Update the blocks. | ||
| 2405 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 2406 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2407 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2408 | |||
| 2409 | // We successfully mapped the pages. | ||
| 2410 | R_SUCCEED(); | ||
| 2411 | } | ||
| 2412 | |||
| 2413 | Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, | ||
| 2414 | KMemoryState state) { | ||
| 2415 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2416 | |||
| 2417 | // Ensure this is a valid unmap request. | ||
| 2418 | const size_t num_pages = pg.GetNumPages(); | ||
| 2419 | const size_t size = num_pages * PageSize; | ||
| 2420 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); | ||
| 2421 | |||
| 2422 | // Lock the table. | ||
| 2423 | KScopedLightLock lk(m_general_lock); | ||
| 2424 | |||
| 2425 | // Check if state allows us to unmap. | ||
| 2426 | size_t num_allocator_blocks; | ||
| 2181 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | 2427 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, |
| 2182 | KMemoryState::All, state, KMemoryPermission::None, | 2428 | KMemoryState::All, state, KMemoryPermission::None, |
| 2183 | KMemoryPermission::None, KMemoryAttribute::All, | 2429 | KMemoryPermission::None, KMemoryAttribute::All, |
| 2184 | KMemoryAttribute::None)); | 2430 | KMemoryAttribute::None)); |
| 2185 | 2431 | ||
| 2432 | // Check that the page group is valid. | ||
| 2433 | R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory); | ||
| 2434 | |||
| 2186 | // Create an update allocator. | 2435 | // Create an update allocator. |
| 2187 | Result allocator_result{ResultSuccess}; | 2436 | Result allocator_result; |
| 2188 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | 2437 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), |
| 2189 | m_memory_block_slab_manager, num_allocator_blocks); | 2438 | m_memory_block_slab_manager, num_allocator_blocks); |
| 2190 | R_TRY(allocator_result); | 2439 | R_TRY(allocator_result); |
| 2191 | 2440 | ||
| 2192 | // Perform the unmap. | 2441 | // We're going to perform an update, so create a helper. |
| 2193 | R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); | 2442 | KScopedPageTableUpdater updater(this); |
| 2443 | |||
| 2444 | // Perform unmapping operation. | ||
| 2445 | const KPageProperties properties = {KMemoryPermission::None, false, false, | ||
| 2446 | DisableMergeAttribute::None}; | ||
| 2447 | R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap)); | ||
| 2194 | 2448 | ||
| 2195 | // Update the blocks. | 2449 | // Update the blocks. |
| 2196 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, | 2450 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, |
| @@ -2550,54 +2804,6 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { | |||
| 2550 | } | 2804 | } |
| 2551 | } | 2805 | } |
| 2552 | 2806 | ||
| 2553 | ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align, | ||
| 2554 | bool is_map_only, VAddr region_start, | ||
| 2555 | size_t region_num_pages, KMemoryState state, | ||
| 2556 | KMemoryPermission perm, PAddr map_addr) { | ||
| 2557 | KScopedLightLock lk(m_general_lock); | ||
| 2558 | |||
| 2559 | R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state), | ||
| 2560 | ResultInvalidCurrentMemory); | ||
| 2561 | R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory); | ||
| 2562 | const VAddr addr{ | ||
| 2563 | AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)}; | ||
| 2564 | R_UNLESS(addr, ResultOutOfMemory); | ||
| 2565 | |||
| 2566 | // Create an update allocator. | ||
| 2567 | Result allocator_result{ResultSuccess}; | ||
| 2568 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2569 | m_memory_block_slab_manager); | ||
| 2570 | |||
| 2571 | if (is_map_only) { | ||
| 2572 | R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); | ||
| 2573 | } else { | ||
| 2574 | // Create a page group tohold the pages we allocate. | ||
| 2575 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 2576 | |||
| 2577 | R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( | ||
| 2578 | &pg, needed_num_pages, | ||
| 2579 | KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); | ||
| 2580 | |||
| 2581 | // Ensure that the page group is closed when we're done working with it. | ||
| 2582 | SCOPE_EXIT({ pg.Close(); }); | ||
| 2583 | |||
| 2584 | // Clear all pages. | ||
| 2585 | for (const auto& it : pg) { | ||
| 2586 | std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), | ||
| 2587 | m_heap_fill_value, it.GetSize()); | ||
| 2588 | } | ||
| 2589 | |||
| 2590 | R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup)); | ||
| 2591 | } | ||
| 2592 | |||
| 2593 | // Update the blocks. | ||
| 2594 | m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm, | ||
| 2595 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2596 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2597 | |||
| 2598 | return addr; | ||
| 2599 | } | ||
| 2600 | |||
| 2601 | Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, | 2807 | Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, |
| 2602 | KMemoryPermission perm, bool is_aligned, | 2808 | KMemoryPermission perm, bool is_aligned, |
| 2603 | bool check_heap) { | 2809 | bool check_heap) { |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 0a454b05b..367dab613 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -24,12 +24,36 @@ class System; | |||
| 24 | 24 | ||
| 25 | namespace Kernel { | 25 | namespace Kernel { |
| 26 | 26 | ||
| 27 | enum class DisableMergeAttribute : u8 { | ||
| 28 | None = (0U << 0), | ||
| 29 | DisableHead = (1U << 0), | ||
| 30 | DisableHeadAndBody = (1U << 1), | ||
| 31 | EnableHeadAndBody = (1U << 2), | ||
| 32 | DisableTail = (1U << 3), | ||
| 33 | EnableTail = (1U << 4), | ||
| 34 | EnableAndMergeHeadBodyTail = (1U << 5), | ||
| 35 | EnableHeadBodyTail = EnableHeadAndBody | EnableTail, | ||
| 36 | DisableHeadBodyTail = DisableHeadAndBody | DisableTail, | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct KPageProperties { | ||
| 40 | KMemoryPermission perm; | ||
| 41 | bool io; | ||
| 42 | bool uncached; | ||
| 43 | DisableMergeAttribute disable_merge_attributes; | ||
| 44 | }; | ||
| 45 | static_assert(std::is_trivial_v<KPageProperties>); | ||
| 46 | static_assert(sizeof(KPageProperties) == sizeof(u32)); | ||
| 47 | |||
| 27 | class KBlockInfoManager; | 48 | class KBlockInfoManager; |
| 28 | class KMemoryBlockManager; | 49 | class KMemoryBlockManager; |
| 29 | class KResourceLimit; | 50 | class KResourceLimit; |
| 30 | class KSystemResource; | 51 | class KSystemResource; |
| 31 | 52 | ||
| 32 | class KPageTable final { | 53 | class KPageTable final { |
| 54 | protected: | ||
| 55 | struct PageLinkedList; | ||
| 56 | |||
| 33 | public: | 57 | public: |
| 34 | enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; | 58 | enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; |
| 35 | 59 | ||
| @@ -57,27 +81,12 @@ public: | |||
| 57 | Result UnmapPhysicalMemory(VAddr addr, size_t size); | 81 | Result UnmapPhysicalMemory(VAddr addr, size_t size); |
| 58 | Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size); | 82 | Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size); |
| 59 | Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); | 83 | Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); |
| 60 | Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state, | ||
| 61 | KMemoryPermission perm); | ||
| 62 | Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, | ||
| 63 | KMemoryState state, KMemoryPermission perm) { | ||
| 64 | R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, | ||
| 65 | this->GetRegionAddress(state), | ||
| 66 | this->GetRegionSize(state) / PageSize, state, perm)); | ||
| 67 | } | ||
| 68 | Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state); | ||
| 69 | Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state); | ||
| 70 | Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm); | 84 | Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm); |
| 71 | KMemoryInfo QueryInfo(VAddr addr); | 85 | KMemoryInfo QueryInfo(VAddr addr); |
| 72 | Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm); | 86 | Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm); |
| 73 | Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr); | 87 | Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr); |
| 74 | Result SetMaxHeapSize(size_t size); | 88 | Result SetMaxHeapSize(size_t size); |
| 75 | Result SetHeapSize(VAddr* out, size_t size); | 89 | Result SetHeapSize(VAddr* out, size_t size); |
| 76 | ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only, | ||
| 77 | VAddr region_start, size_t region_num_pages, | ||
| 78 | KMemoryState state, KMemoryPermission perm, | ||
| 79 | PAddr map_addr = 0); | ||
| 80 | |||
| 81 | Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, | 90 | Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, |
| 82 | KMemoryPermission perm, bool is_aligned, bool check_heap); | 91 | KMemoryPermission perm, bool is_aligned, bool check_heap); |
| 83 | Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap); | 92 | Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap); |
| @@ -113,6 +122,40 @@ public: | |||
| 113 | 122 | ||
| 114 | bool CanContain(VAddr addr, size_t size, KMemoryState state) const; | 123 | bool CanContain(VAddr addr, size_t size, KMemoryState state) const; |
| 115 | 124 | ||
| 125 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 126 | KPhysicalAddress phys_addr, KProcessAddress region_start, | ||
| 127 | size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { | ||
| 128 | R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start, | ||
| 129 | region_num_pages, state, perm)); | ||
| 130 | } | ||
| 131 | |||
| 132 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 133 | KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) { | ||
| 134 | R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, | ||
| 135 | this->GetRegionAddress(state), | ||
| 136 | this->GetRegionSize(state) / PageSize, state, perm)); | ||
| 137 | } | ||
| 138 | |||
| 139 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state, | ||
| 140 | KMemoryPermission perm) { | ||
| 141 | R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false, | ||
| 142 | this->GetRegionAddress(state), | ||
| 143 | this->GetRegionSize(state) / PageSize, state, perm)); | ||
| 144 | } | ||
| 145 | |||
| 146 | Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, | ||
| 147 | KMemoryPermission perm); | ||
| 148 | Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state); | ||
| 149 | |||
| 150 | Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, | ||
| 151 | KProcessAddress region_start, size_t region_num_pages, KMemoryState state, | ||
| 152 | KMemoryPermission perm); | ||
| 153 | Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state, | ||
| 154 | KMemoryPermission perm); | ||
| 155 | Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state); | ||
| 156 | void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, | ||
| 157 | const KPageGroup& pg); | ||
| 158 | |||
| 116 | protected: | 159 | protected: |
| 117 | struct PageLinkedList { | 160 | struct PageLinkedList { |
| 118 | private: | 161 | private: |
| @@ -166,11 +209,9 @@ private: | |||
| 166 | static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = | 209 | static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = |
| 167 | KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; | 210 | KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; |
| 168 | 211 | ||
| 169 | Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); | 212 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, |
| 170 | Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, | 213 | KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, |
| 171 | bool is_pa_valid, VAddr region_start, size_t region_num_pages, | 214 | size_t region_num_pages, KMemoryState state, KMemoryPermission perm); |
| 172 | KMemoryState state, KMemoryPermission perm); | ||
| 173 | Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list); | ||
| 174 | bool IsRegionContiguous(VAddr addr, u64 size) const; | 215 | bool IsRegionContiguous(VAddr addr, u64 size) const; |
| 175 | void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list); | 216 | void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list); |
| 176 | KMemoryInfo QueryInfoImpl(VAddr addr); | 217 | KMemoryInfo QueryInfoImpl(VAddr addr); |
| @@ -265,6 +306,11 @@ private: | |||
| 265 | void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, | 306 | void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, |
| 266 | size_t size, KMemoryPermission prot_perm); | 307 | size_t size, KMemoryPermission prot_perm); |
| 267 | 308 | ||
| 309 | Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 310 | size_t num_pages, KMemoryPermission perm); | ||
| 311 | Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 312 | const KPageGroup& pg, const KPageProperties properties, bool reuse_ll); | ||
| 313 | |||
| 268 | mutable KLightLock m_general_lock; | 314 | mutable KLightLock m_general_lock; |
| 269 | mutable KLightLock m_map_physical_memory_lock; | 315 | mutable KLightLock m_map_physical_memory_lock; |
| 270 | 316 | ||
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index a1abf5d68..e201bb0cd 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -417,9 +417,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: | |||
| 417 | } | 417 | } |
| 418 | 418 | ||
| 419 | void KProcess::Run(s32 main_thread_priority, u64 stack_size) { | 419 | void KProcess::Run(s32 main_thread_priority, u64 stack_size) { |
| 420 | AllocateMainThreadStack(stack_size); | 420 | ASSERT(AllocateMainThreadStack(stack_size) == ResultSuccess); |
| 421 | resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); | 421 | resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); |
| 422 | resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size); | ||
| 423 | 422 | ||
| 424 | const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; | 423 | const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; |
| 425 | ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); | 424 | ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); |
| @@ -675,20 +674,31 @@ void KProcess::ChangeState(State new_state) { | |||
| 675 | } | 674 | } |
| 676 | 675 | ||
| 677 | Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { | 676 | Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { |
| 678 | ASSERT(stack_size); | 677 | // Ensure that we haven't already allocated stack. |
| 679 | 678 | ASSERT(main_thread_stack_size == 0); | |
| 680 | // The kernel always ensures that the given stack size is page aligned. | 679 | |
| 681 | main_thread_stack_size = Common::AlignUp(stack_size, PageSize); | 680 | // Ensure that we're allocating a valid stack. |
| 682 | 681 | stack_size = Common::AlignUp(stack_size, PageSize); | |
| 683 | const VAddr start{page_table.GetStackRegionStart()}; | 682 | // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory); |
| 684 | const std::size_t size{page_table.GetStackRegionEnd() - start}; | 683 | R_UNLESS(stack_size + image_size >= image_size, ResultOutOfMemory); |
| 685 | 684 | ||
| 686 | CASCADE_RESULT(main_thread_stack_top, | 685 | // Place a tentative reservation of memory for our new stack. |
| 687 | page_table.AllocateAndMapMemory( | 686 | KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax, |
| 688 | main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, | 687 | stack_size); |
| 689 | KMemoryState::Stack, KMemoryPermission::UserReadWrite)); | 688 | R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached); |
| 689 | |||
| 690 | // Allocate and map our stack. | ||
| 691 | if (stack_size) { | ||
| 692 | KProcessAddress stack_bottom; | ||
| 693 | R_TRY(page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize, | ||
| 694 | KMemoryState::Stack, KMemoryPermission::UserReadWrite)); | ||
| 695 | |||
| 696 | main_thread_stack_top = stack_bottom + stack_size; | ||
| 697 | main_thread_stack_size = stack_size; | ||
| 698 | } | ||
| 690 | 699 | ||
| 691 | main_thread_stack_top += main_thread_stack_size; | 700 | // We succeeded! Commit our memory reservation. |
| 701 | mem_reservation.Commit(); | ||
| 692 | 702 | ||
| 693 | R_SUCCEED(); | 703 | R_SUCCEED(); |
| 694 | } | 704 | } |
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index 3cf2b5d91..df505edfe 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp | |||
| @@ -94,15 +94,15 @@ Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t m | |||
| 94 | R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); | 94 | R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared, | 97 | return target_process.PageTable().MapPageGroup(address, *page_group, KMemoryState::Shared, |
| 98 | ConvertToKMemoryPermission(map_perm)); | 98 | ConvertToKMemoryPermission(map_perm)); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { | 101 | Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { |
| 102 | // Validate the size. | 102 | // Validate the size. |
| 103 | R_UNLESS(size == unmap_size, ResultInvalidSize); | 103 | R_UNLESS(size == unmap_size, ResultInvalidSize); |
| 104 | 104 | ||
| 105 | return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared); | 105 | return target_process.PageTable().UnmapPageGroup(address, *page_group, KMemoryState::Shared); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | } // namespace Kernel | 108 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index aca442196..67fa5d71c 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1492,8 +1492,8 @@ static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle p | |||
| 1492 | KMemoryAttribute::All, KMemoryAttribute::None)); | 1492 | KMemoryAttribute::All, KMemoryAttribute::None)); |
| 1493 | 1493 | ||
| 1494 | // Map the group. | 1494 | // Map the group. |
| 1495 | R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, | 1495 | R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode, |
| 1496 | KMemoryPermission::UserReadWrite)); | 1496 | KMemoryPermission::UserReadWrite)); |
| 1497 | 1497 | ||
| 1498 | return ResultSuccess; | 1498 | return ResultSuccess; |
| 1499 | } | 1499 | } |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 2f871de31..5713f1288 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -272,6 +272,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | |||
| 272 | } | 272 | } |
| 273 | break; | 273 | break; |
| 274 | case Core::HID::NpadStyleIndex::JoyconLeft: | 274 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 275 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 276 | shared_memory->fullkey_color.fullkey = body_colors.left; | ||
| 275 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | 277 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; |
| 276 | shared_memory->joycon_color.left = body_colors.left; | 278 | shared_memory->joycon_color.left = body_colors.left; |
| 277 | shared_memory->battery_level_dual = battery_level.left.battery_level; | 279 | shared_memory->battery_level_dual = battery_level.left.battery_level; |
| @@ -285,6 +287,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | |||
| 285 | shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); | 287 | shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); |
| 286 | break; | 288 | break; |
| 287 | case Core::HID::NpadStyleIndex::JoyconRight: | 289 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 290 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 291 | shared_memory->fullkey_color.fullkey = body_colors.right; | ||
| 288 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | 292 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; |
| 289 | shared_memory->joycon_color.right = body_colors.right; | 293 | shared_memory->joycon_color.right = body_colors.right; |
| 290 | shared_memory->battery_level_right = battery_level.right.battery_level; | 294 | shared_memory->battery_level_right = battery_level.right.battery_level; |
| @@ -332,6 +336,20 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | |||
| 332 | 336 | ||
| 333 | controller.is_connected = true; | 337 | controller.is_connected = true; |
| 334 | controller.device->Connect(); | 338 | controller.device->Connect(); |
| 339 | controller.device->SetLedPattern(); | ||
| 340 | if (controller_type == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 341 | if (controller.is_dual_left_connected) { | ||
| 342 | controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::LeftIndex, | ||
| 343 | Common::Input::PollingMode::Active); | ||
| 344 | } | ||
| 345 | if (controller.is_dual_right_connected) { | ||
| 346 | controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 347 | Common::Input::PollingMode::Active); | ||
| 348 | } | ||
| 349 | } else { | ||
| 350 | controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, | ||
| 351 | Common::Input::PollingMode::Active); | ||
| 352 | } | ||
| 335 | SignalStyleSetChangedEvent(npad_id); | 353 | SignalStyleSetChangedEvent(npad_id); |
| 336 | WriteEmptyEntry(controller.shared_memory); | 354 | WriteEmptyEntry(controller.shared_memory); |
| 337 | } | 355 | } |
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index e5e50845f..17252a84a 100644 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp | |||
| @@ -297,13 +297,13 @@ void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) { | |||
| 297 | 297 | ||
| 298 | const auto parameters{rp.PopRaw<Parameters>()}; | 298 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 299 | 299 | ||
| 300 | LOG_INFO(Service_HID, | 300 | LOG_DEBUG(Service_HID, |
| 301 | "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " | 301 | "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " |
| 302 | "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", | 302 | "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", |
| 303 | parameters.enable, parameters.bus_handle.abstracted_pad_id, | 303 | parameters.enable, parameters.bus_handle.abstracted_pad_id, |
| 304 | parameters.bus_handle.bus_type, parameters.bus_handle.internal_index, | 304 | parameters.bus_handle.bus_type, parameters.bus_handle.internal_index, |
| 305 | parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, | 305 | parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, |
| 306 | parameters.applet_resource_user_id); | 306 | parameters.applet_resource_user_id); |
| 307 | 307 | ||
| 308 | const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); | 308 | const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); |
| 309 | 309 | ||
| @@ -326,11 +326,11 @@ void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) { | |||
| 326 | IPC::RequestParser rp{ctx}; | 326 | IPC::RequestParser rp{ctx}; |
| 327 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; | 327 | const auto bus_handle_{rp.PopRaw<BusHandle>()}; |
| 328 | 328 | ||
| 329 | LOG_INFO(Service_HID, | 329 | LOG_DEBUG(Service_HID, |
| 330 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " | 330 | "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " |
| 331 | "is_valid={}", | 331 | "is_valid={}", |
| 332 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, | 332 | bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, |
| 333 | bus_handle_.player_number, bus_handle_.is_valid); | 333 | bus_handle_.player_number, bus_handle_.is_valid); |
| 334 | 334 | ||
| 335 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); | 335 | const auto device_index = GetDeviceIndexFromHandle(bus_handle_); |
| 336 | 336 | ||
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp index 57f1a2a26..78ed47014 100644 --- a/src/core/hle/service/hid/hidbus/ringcon.cpp +++ b/src/core/hle/service/hid/hidbus/ringcon.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "core/hid/emulated_devices.h" | 4 | #include "core/hid/emulated_controller.h" |
| 5 | #include "core/hid/hid_core.h" | 5 | #include "core/hid/hid_core.h" |
| 6 | #include "core/hle/kernel/k_event.h" | 6 | #include "core/hle/kernel/k_event.h" |
| 7 | #include "core/hle/kernel/k_readable_event.h" | 7 | #include "core/hle/kernel/k_readable_event.h" |
| @@ -12,16 +12,20 @@ namespace Service::HID { | |||
| 12 | RingController::RingController(Core::HID::HIDCore& hid_core_, | 12 | RingController::RingController(Core::HID::HIDCore& hid_core_, |
| 13 | KernelHelpers::ServiceContext& service_context_) | 13 | KernelHelpers::ServiceContext& service_context_) |
| 14 | : HidbusBase(service_context_) { | 14 | : HidbusBase(service_context_) { |
| 15 | input = hid_core_.GetEmulatedDevices(); | 15 | input = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | RingController::~RingController() = default; | 18 | RingController::~RingController() = default; |
| 19 | 19 | ||
| 20 | void RingController::OnInit() { | 20 | void RingController::OnInit() { |
| 21 | input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 22 | Common::Input::PollingMode::Ring); | ||
| 21 | return; | 23 | return; |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | void RingController::OnRelease() { | 26 | void RingController::OnRelease() { |
| 27 | input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 28 | Common::Input::PollingMode::Active); | ||
| 25 | return; | 29 | return; |
| 26 | }; | 30 | }; |
| 27 | 31 | ||
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h index b37df50ac..845ce85a5 100644 --- a/src/core/hle/service/hid/hidbus/ringcon.h +++ b/src/core/hle/service/hid/hidbus/ringcon.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "core/hle/service/hid/hidbus/hidbus_base.h" | 9 | #include "core/hle/service/hid/hidbus/hidbus_base.h" |
| 10 | 10 | ||
| 11 | namespace Core::HID { | 11 | namespace Core::HID { |
| 12 | class EmulatedDevices; | 12 | class EmulatedController; |
| 13 | } // namespace Core::HID | 13 | } // namespace Core::HID |
| 14 | 14 | ||
| 15 | namespace Service::HID { | 15 | namespace Service::HID { |
| @@ -248,6 +248,6 @@ private: | |||
| 248 | .zero = {.value = idle_value, .crc = 225}, | 248 | .zero = {.value = idle_value, .crc = 225}, |
| 249 | }; | 249 | }; |
| 250 | 250 | ||
| 251 | Core::HID::EmulatedDevices* input; | 251 | Core::HID::EmulatedController* input; |
| 252 | }; | 252 | }; |
| 253 | } // namespace Service::HID | 253 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 6a3453457..52f402c56 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -108,6 +108,8 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { | |||
| 108 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | 108 | auto result = IsIrCameraHandleValid(parameters.camera_handle); |
| 109 | if (result.IsSuccess()) { | 109 | if (result.IsSuccess()) { |
| 110 | // TODO: Stop Image processor | 110 | // TODO: Stop Image processor |
| 111 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 112 | Common::Input::PollingMode::Active); | ||
| 111 | result = ResultSuccess; | 113 | result = ResultSuccess; |
| 112 | } | 114 | } |
| 113 | 115 | ||
| @@ -139,6 +141,8 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { | |||
| 139 | MakeProcessor<MomentProcessor>(parameters.camera_handle, device); | 141 | MakeProcessor<MomentProcessor>(parameters.camera_handle, device); |
| 140 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); | 142 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); |
| 141 | image_transfer_processor.SetConfig(parameters.processor_config); | 143 | image_transfer_processor.SetConfig(parameters.processor_config); |
| 144 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 145 | Common::Input::PollingMode::IR); | ||
| 142 | } | 146 | } |
| 143 | 147 | ||
| 144 | IPC::ResponseBuilder rb{ctx, 2}; | 148 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -170,6 +174,8 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { | |||
| 170 | auto& image_transfer_processor = | 174 | auto& image_transfer_processor = |
| 171 | GetProcessor<ClusteringProcessor>(parameters.camera_handle); | 175 | GetProcessor<ClusteringProcessor>(parameters.camera_handle); |
| 172 | image_transfer_processor.SetConfig(parameters.processor_config); | 176 | image_transfer_processor.SetConfig(parameters.processor_config); |
| 177 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 178 | Common::Input::PollingMode::IR); | ||
| 173 | } | 179 | } |
| 174 | 180 | ||
| 175 | IPC::ResponseBuilder rb{ctx, 2}; | 181 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -219,6 +225,8 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { | |||
| 219 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | 225 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); |
| 220 | image_transfer_processor.SetConfig(parameters.processor_config); | 226 | image_transfer_processor.SetConfig(parameters.processor_config); |
| 221 | image_transfer_processor.SetTransferMemoryPointer(transfer_memory); | 227 | image_transfer_processor.SetTransferMemoryPointer(transfer_memory); |
| 228 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 229 | Common::Input::PollingMode::IR); | ||
| 222 | } | 230 | } |
| 223 | 231 | ||
| 224 | IPC::ResponseBuilder rb{ctx, 2}; | 232 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -294,6 +302,8 @@ void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { | |||
| 294 | auto& image_transfer_processor = | 302 | auto& image_transfer_processor = |
| 295 | GetProcessor<TeraPluginProcessor>(parameters.camera_handle); | 303 | GetProcessor<TeraPluginProcessor>(parameters.camera_handle); |
| 296 | image_transfer_processor.SetConfig(parameters.processor_config); | 304 | image_transfer_processor.SetConfig(parameters.processor_config); |
| 305 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 306 | Common::Input::PollingMode::IR); | ||
| 297 | } | 307 | } |
| 298 | 308 | ||
| 299 | IPC::ResponseBuilder rb{ctx, 2}; | 309 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -343,6 +353,8 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { | |||
| 343 | MakeProcessor<PointingProcessor>(camera_handle, device); | 353 | MakeProcessor<PointingProcessor>(camera_handle, device); |
| 344 | auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); | 354 | auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); |
| 345 | image_transfer_processor.SetConfig(processor_config); | 355 | image_transfer_processor.SetConfig(processor_config); |
| 356 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 357 | Common::Input::PollingMode::IR); | ||
| 346 | } | 358 | } |
| 347 | 359 | ||
| 348 | IPC::ResponseBuilder rb{ctx, 2}; | 360 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -453,6 +465,8 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { | |||
| 453 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | 465 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); |
| 454 | image_transfer_processor.SetConfig(parameters.processor_config); | 466 | image_transfer_processor.SetConfig(parameters.processor_config); |
| 455 | image_transfer_processor.SetTransferMemoryPointer(transfer_memory); | 467 | image_transfer_processor.SetTransferMemoryPointer(transfer_memory); |
| 468 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 469 | Common::Input::PollingMode::IR); | ||
| 456 | } | 470 | } |
| 457 | 471 | ||
| 458 | IPC::ResponseBuilder rb{ctx, 2}; | 472 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -479,6 +493,8 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { | |||
| 479 | MakeProcessor<IrLedProcessor>(camera_handle, device); | 493 | MakeProcessor<IrLedProcessor>(camera_handle, device); |
| 480 | auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); | 494 | auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); |
| 481 | image_transfer_processor.SetConfig(processor_config); | 495 | image_transfer_processor.SetConfig(processor_config); |
| 496 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 497 | Common::Input::PollingMode::IR); | ||
| 482 | } | 498 | } |
| 483 | 499 | ||
| 484 | IPC::ResponseBuilder rb{ctx, 2}; | 500 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -504,6 +520,8 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { | |||
| 504 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | 520 | auto result = IsIrCameraHandleValid(parameters.camera_handle); |
| 505 | if (result.IsSuccess()) { | 521 | if (result.IsSuccess()) { |
| 506 | // TODO: Stop image processor async | 522 | // TODO: Stop image processor async |
| 523 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 524 | Common::Input::PollingMode::Active); | ||
| 507 | result = ResultSuccess; | 525 | result = ResultSuccess; |
| 508 | } | 526 | } |
| 509 | 527 | ||
diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp index 78578f723..9a3234e8c 100644 --- a/src/core/hle/service/nfc/nfc_device.cpp +++ b/src/core/hle/service/nfc/nfc_device.cpp | |||
| @@ -130,7 +130,9 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) { | |||
| 130 | return WrongDeviceState; | 130 | return WrongDeviceState; |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { | 133 | if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 134 | Common::Input::PollingMode::NFC) != | ||
| 135 | Common::Input::DriverResult::Success) { | ||
| 134 | LOG_ERROR(Service_NFC, "Nfc not supported"); | 136 | LOG_ERROR(Service_NFC, "Nfc not supported"); |
| 135 | return NfcDisabled; | 137 | return NfcDisabled; |
| 136 | } | 138 | } |
| @@ -141,7 +143,8 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) { | |||
| 141 | } | 143 | } |
| 142 | 144 | ||
| 143 | Result NfcDevice::StopDetection() { | 145 | Result NfcDevice::StopDetection() { |
| 144 | npad_device->SetPollingMode(Common::Input::PollingMode::Active); | 146 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 147 | Common::Input::PollingMode::Active); | ||
| 145 | 148 | ||
| 146 | if (device_state == NFP::DeviceState::Initialized) { | 149 | if (device_state == NFP::DeviceState::Initialized) { |
| 147 | return ResultSuccess; | 150 | return ResultSuccess; |
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index c860fd1a1..e67a76f55 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp | |||
| @@ -152,7 +152,9 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) { | |||
| 152 | return WrongDeviceState; | 152 | return WrongDeviceState; |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { | 155 | if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 156 | Common::Input::PollingMode::NFC) != | ||
| 157 | Common::Input::DriverResult::Success) { | ||
| 156 | LOG_ERROR(Service_NFP, "Nfc not supported"); | 158 | LOG_ERROR(Service_NFP, "Nfc not supported"); |
| 157 | return NfcDisabled; | 159 | return NfcDisabled; |
| 158 | } | 160 | } |
| @@ -163,7 +165,8 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) { | |||
| 163 | } | 165 | } |
| 164 | 166 | ||
| 165 | Result NfpDevice::StopDetection() { | 167 | Result NfpDevice::StopDetection() { |
| 166 | npad_device->SetPollingMode(Common::Input::PollingMode::Active); | 168 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 169 | Common::Input::PollingMode::Active); | ||
| 167 | 170 | ||
| 168 | if (device_state == DeviceState::Initialized) { | 171 | if (device_state == DeviceState::Initialized) { |
| 169 | return ResultSuccess; | 172 | return ResultSuccess; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 4e605fae4..af9660b55 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -440,7 +440,7 @@ struct Memory::Impl { | |||
| 440 | } | 440 | } |
| 441 | 441 | ||
| 442 | if (Settings::IsFastmemEnabled()) { | 442 | if (Settings::IsFastmemEnabled()) { |
| 443 | const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached; | 443 | const bool is_read_enable = Settings::IsGPULevelHigh() || !cached; |
| 444 | system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); | 444 | system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); |
| 445 | } | 445 | } |
| 446 | 446 | ||
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index cef2c4d52..e3b627e4f 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -51,8 +51,29 @@ endif() | |||
| 51 | 51 | ||
| 52 | if (ENABLE_SDL2) | 52 | if (ENABLE_SDL2) |
| 53 | target_sources(input_common PRIVATE | 53 | target_sources(input_common PRIVATE |
| 54 | drivers/joycon.cpp | ||
| 55 | drivers/joycon.h | ||
| 54 | drivers/sdl_driver.cpp | 56 | drivers/sdl_driver.cpp |
| 55 | drivers/sdl_driver.h | 57 | drivers/sdl_driver.h |
| 58 | helpers/joycon_driver.cpp | ||
| 59 | helpers/joycon_driver.h | ||
| 60 | helpers/joycon_protocol/calibration.cpp | ||
| 61 | helpers/joycon_protocol/calibration.h | ||
| 62 | helpers/joycon_protocol/common_protocol.cpp | ||
| 63 | helpers/joycon_protocol/common_protocol.h | ||
| 64 | helpers/joycon_protocol/generic_functions.cpp | ||
| 65 | helpers/joycon_protocol/generic_functions.h | ||
| 66 | helpers/joycon_protocol/joycon_types.h | ||
| 67 | helpers/joycon_protocol/irs.cpp | ||
| 68 | helpers/joycon_protocol/irs.h | ||
| 69 | helpers/joycon_protocol/nfc.cpp | ||
| 70 | helpers/joycon_protocol/nfc.h | ||
| 71 | helpers/joycon_protocol/poller.cpp | ||
| 72 | helpers/joycon_protocol/poller.h | ||
| 73 | helpers/joycon_protocol/ringcon.cpp | ||
| 74 | helpers/joycon_protocol/ringcon.h | ||
| 75 | helpers/joycon_protocol/rumble.cpp | ||
| 76 | helpers/joycon_protocol/rumble.h | ||
| 56 | ) | 77 | ) |
| 57 | target_link_libraries(input_common PRIVATE SDL2::SDL2) | 78 | target_link_libraries(input_common PRIVATE SDL2::SDL2) |
| 58 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) | 79 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) |
diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp index fad9177dc..04970f635 100644 --- a/src/input_common/drivers/camera.cpp +++ b/src/input_common/drivers/camera.cpp | |||
| @@ -72,11 +72,11 @@ std::size_t Camera::getImageHeight() const { | |||
| 72 | } | 72 | } |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | Common::Input::CameraError Camera::SetCameraFormat( | 75 | Common::Input::DriverResult Camera::SetCameraFormat( |
| 76 | [[maybe_unused]] const PadIdentifier& identifier_, | 76 | [[maybe_unused]] const PadIdentifier& identifier_, |
| 77 | const Common::Input::CameraFormat camera_format) { | 77 | const Common::Input::CameraFormat camera_format) { |
| 78 | status.format = camera_format; | 78 | status.format = camera_format; |
| 79 | return Common::Input::CameraError::None; | 79 | return Common::Input::DriverResult::Success; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | } // namespace InputCommon | 82 | } // namespace InputCommon |
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h index ead3e0fde..24b27e325 100644 --- a/src/input_common/drivers/camera.h +++ b/src/input_common/drivers/camera.h | |||
| @@ -22,8 +22,8 @@ public: | |||
| 22 | std::size_t getImageWidth() const; | 22 | std::size_t getImageWidth() const; |
| 23 | std::size_t getImageHeight() const; | 23 | std::size_t getImageHeight() const; |
| 24 | 24 | ||
| 25 | Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, | 25 | Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_, |
| 26 | Common::Input::CameraFormat camera_format) override; | 26 | Common::Input::CameraFormat camera_format) override; |
| 27 | 27 | ||
| 28 | private: | 28 | private: |
| 29 | Common::Input::CameraStatus status{}; | 29 | Common::Input::CameraStatus status{}; |
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 826fa2109..ecb3e9dc2 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp | |||
| @@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) { | |||
| 324 | return true; | 324 | return true; |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | Common::Input::VibrationError GCAdapter::SetVibration( | 327 | Common::Input::DriverResult GCAdapter::SetVibration( |
| 328 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { | 328 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { |
| 329 | const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; | 329 | const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; |
| 330 | const auto processed_amplitude = | 330 | const auto processed_amplitude = |
| @@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration( | |||
| 333 | pads[identifier.port].rumble_amplitude = processed_amplitude; | 333 | pads[identifier.port].rumble_amplitude = processed_amplitude; |
| 334 | 334 | ||
| 335 | if (!rumble_enabled) { | 335 | if (!rumble_enabled) { |
| 336 | return Common::Input::VibrationError::Disabled; | 336 | return Common::Input::DriverResult::Disabled; |
| 337 | } | 337 | } |
| 338 | return Common::Input::VibrationError::None; | 338 | return Common::Input::DriverResult::Success; |
| 339 | } | 339 | } |
| 340 | 340 | ||
| 341 | bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { | 341 | bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { |
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index b5270fd0b..3c2eb376d 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | explicit GCAdapter(std::string input_engine_); | 25 | explicit GCAdapter(std::string input_engine_); |
| 26 | ~GCAdapter() override; | 26 | ~GCAdapter() override; |
| 27 | 27 | ||
| 28 | Common::Input::VibrationError SetVibration( | 28 | Common::Input::DriverResult SetVibration( |
| 29 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | 29 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; |
| 30 | 30 | ||
| 31 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; | 31 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; |
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp new file mode 100644 index 000000000..40cda400d --- /dev/null +++ b/src/input_common/drivers/joycon.cpp | |||
| @@ -0,0 +1,678 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <fmt/format.h> | ||
| 5 | |||
| 6 | #include "common/param_package.h" | ||
| 7 | #include "common/polyfill_ranges.h" | ||
| 8 | #include "common/settings.h" | ||
| 9 | #include "common/thread.h" | ||
| 10 | #include "input_common/drivers/joycon.h" | ||
| 11 | #include "input_common/helpers/joycon_driver.h" | ||
| 12 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 13 | |||
| 14 | namespace InputCommon { | ||
| 15 | |||
| 16 | Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) { | ||
| 17 | // Avoid conflicting with SDL driver | ||
| 18 | if (!Settings::values.enable_joycon_driver) { | ||
| 19 | return; | ||
| 20 | } | ||
| 21 | LOG_INFO(Input, "Joycon driver Initialization started"); | ||
| 22 | const int init_res = SDL_hid_init(); | ||
| 23 | if (init_res == 0) { | ||
| 24 | Setup(); | ||
| 25 | } else { | ||
| 26 | LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | Joycons::~Joycons() { | ||
| 31 | Reset(); | ||
| 32 | } | ||
| 33 | |||
| 34 | void Joycons::Reset() { | ||
| 35 | scan_thread = {}; | ||
| 36 | for (const auto& device : left_joycons) { | ||
| 37 | if (!device) { | ||
| 38 | continue; | ||
| 39 | } | ||
| 40 | device->Stop(); | ||
| 41 | } | ||
| 42 | for (const auto& device : right_joycons) { | ||
| 43 | if (!device) { | ||
| 44 | continue; | ||
| 45 | } | ||
| 46 | device->Stop(); | ||
| 47 | } | ||
| 48 | SDL_hid_exit(); | ||
| 49 | } | ||
| 50 | |||
| 51 | void Joycons::Setup() { | ||
| 52 | u32 port = 0; | ||
| 53 | PreSetController(GetIdentifier(0, Joycon::ControllerType::None)); | ||
| 54 | for (auto& device : left_joycons) { | ||
| 55 | PreSetController(GetIdentifier(port, Joycon::ControllerType::Left)); | ||
| 56 | device = std::make_shared<Joycon::JoyconDriver>(port++); | ||
| 57 | } | ||
| 58 | port = 0; | ||
| 59 | for (auto& device : right_joycons) { | ||
| 60 | PreSetController(GetIdentifier(port, Joycon::ControllerType::Right)); | ||
| 61 | device = std::make_shared<Joycon::JoyconDriver>(port++); | ||
| 62 | } | ||
| 63 | |||
| 64 | scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); }); | ||
| 65 | } | ||
| 66 | |||
| 67 | void Joycons::ScanThread(std::stop_token stop_token) { | ||
| 68 | constexpr u16 nintendo_vendor_id = 0x057e; | ||
| 69 | Common::SetCurrentThreadName("JoyconScanThread"); | ||
| 70 | while (!stop_token.stop_requested()) { | ||
| 71 | SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0); | ||
| 72 | SDL_hid_device_info* cur_dev = devs; | ||
| 73 | |||
| 74 | while (cur_dev) { | ||
| 75 | if (IsDeviceNew(cur_dev)) { | ||
| 76 | LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id, | ||
| 77 | cur_dev->product_id); | ||
| 78 | RegisterNewDevice(cur_dev); | ||
| 79 | } | ||
| 80 | cur_dev = cur_dev->next; | ||
| 81 | } | ||
| 82 | |||
| 83 | SDL_hid_free_enumeration(devs); | ||
| 84 | std::this_thread::sleep_for(std::chrono::seconds(5)); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { | ||
| 89 | Joycon::ControllerType type{}; | ||
| 90 | Joycon::SerialNumber serial_number{}; | ||
| 91 | |||
| 92 | const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type); | ||
| 93 | if (result != Joycon::DriverResult::Success) { | ||
| 94 | return false; | ||
| 95 | } | ||
| 96 | |||
| 97 | const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number); | ||
| 98 | if (result2 != Joycon::DriverResult::Success) { | ||
| 99 | return false; | ||
| 100 | } | ||
| 101 | |||
| 102 | auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) { | ||
| 103 | if (!device) { | ||
| 104 | return false; | ||
| 105 | } | ||
| 106 | if (!device->IsConnected()) { | ||
| 107 | return false; | ||
| 108 | } | ||
| 109 | if (device->GetHandleSerialNumber() != serial_number) { | ||
| 110 | return false; | ||
| 111 | } | ||
| 112 | return true; | ||
| 113 | }; | ||
| 114 | |||
| 115 | // Check if device already exist | ||
| 116 | switch (type) { | ||
| 117 | case Joycon::ControllerType::Left: | ||
| 118 | for (const auto& device : left_joycons) { | ||
| 119 | if (is_handle_identical(device)) { | ||
| 120 | return false; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | break; | ||
| 124 | case Joycon::ControllerType::Right: | ||
| 125 | for (const auto& device : right_joycons) { | ||
| 126 | if (is_handle_identical(device)) { | ||
| 127 | return false; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | break; | ||
| 131 | default: | ||
| 132 | return false; | ||
| 133 | } | ||
| 134 | |||
| 135 | return true; | ||
| 136 | } | ||
| 137 | |||
| 138 | void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) { | ||
| 139 | Joycon::ControllerType type{}; | ||
| 140 | auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type); | ||
| 141 | auto handle = GetNextFreeHandle(type); | ||
| 142 | if (handle == nullptr) { | ||
| 143 | LOG_WARNING(Input, "No free handles available"); | ||
| 144 | return; | ||
| 145 | } | ||
| 146 | if (result == Joycon::DriverResult::Success) { | ||
| 147 | result = handle->RequestDeviceAccess(device_info); | ||
| 148 | } | ||
| 149 | if (result == Joycon::DriverResult::Success) { | ||
| 150 | LOG_WARNING(Input, "Initialize device"); | ||
| 151 | |||
| 152 | const std::size_t port = handle->GetDevicePort(); | ||
| 153 | const Joycon::JoyconCallbacks callbacks{ | ||
| 154 | .on_battery_data = {[this, port, type](Joycon::Battery value) { | ||
| 155 | OnBatteryUpdate(port, type, value); | ||
| 156 | }}, | ||
| 157 | .on_color_data = {[this, port, type](Joycon::Color value) { | ||
| 158 | OnColorUpdate(port, type, value); | ||
| 159 | }}, | ||
| 160 | .on_button_data = {[this, port, type](int id, bool value) { | ||
| 161 | OnButtonUpdate(port, type, id, value); | ||
| 162 | }}, | ||
| 163 | .on_stick_data = {[this, port, type](int id, f32 value) { | ||
| 164 | OnStickUpdate(port, type, id, value); | ||
| 165 | }}, | ||
| 166 | .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) { | ||
| 167 | OnMotionUpdate(port, type, id, value); | ||
| 168 | }}, | ||
| 169 | .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, | ||
| 170 | .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) { | ||
| 171 | OnAmiiboUpdate(port, amiibo_data); | ||
| 172 | }}, | ||
| 173 | .on_camera_data = {[this, port](const std::vector<u8>& camera_data, | ||
| 174 | Joycon::IrsResolution format) { | ||
| 175 | OnCameraUpdate(port, camera_data, format); | ||
| 176 | }}, | ||
| 177 | }; | ||
| 178 | |||
| 179 | handle->InitializeDevice(); | ||
| 180 | handle->SetCallbacks(callbacks); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle( | ||
| 185 | Joycon::ControllerType type) const { | ||
| 186 | if (type == Joycon::ControllerType::Left) { | ||
| 187 | const auto unconnected_device = | ||
| 188 | std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); }); | ||
| 189 | if (unconnected_device != left_joycons.end()) { | ||
| 190 | return *unconnected_device; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | if (type == Joycon::ControllerType::Right) { | ||
| 194 | const auto unconnected_device = std::ranges::find_if( | ||
| 195 | right_joycons, [](auto& device) { return !device->IsConnected(); }); | ||
| 196 | |||
| 197 | if (unconnected_device != right_joycons.end()) { | ||
| 198 | return *unconnected_device; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | return nullptr; | ||
| 202 | } | ||
| 203 | |||
| 204 | bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) { | ||
| 205 | const auto handle = GetHandle(identifier); | ||
| 206 | if (handle == nullptr) { | ||
| 207 | return false; | ||
| 208 | } | ||
| 209 | return handle->IsVibrationEnabled(); | ||
| 210 | } | ||
| 211 | |||
| 212 | Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier, | ||
| 213 | const Common::Input::VibrationStatus& vibration) { | ||
| 214 | const Joycon::VibrationValue native_vibration{ | ||
| 215 | .low_amplitude = vibration.low_amplitude, | ||
| 216 | .low_frequency = vibration.low_frequency, | ||
| 217 | .high_amplitude = vibration.high_amplitude, | ||
| 218 | .high_frequency = vibration.high_frequency, | ||
| 219 | }; | ||
| 220 | auto handle = GetHandle(identifier); | ||
| 221 | if (handle == nullptr) { | ||
| 222 | return Common::Input::DriverResult::InvalidHandle; | ||
| 223 | } | ||
| 224 | |||
| 225 | handle->SetVibration(native_vibration); | ||
| 226 | return Common::Input::DriverResult::Success; | ||
| 227 | } | ||
| 228 | |||
| 229 | Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier, | ||
| 230 | const Common::Input::LedStatus& led_status) { | ||
| 231 | auto handle = GetHandle(identifier); | ||
| 232 | if (handle == nullptr) { | ||
| 233 | return Common::Input::DriverResult::InvalidHandle; | ||
| 234 | } | ||
| 235 | int led_config = led_status.led_1 ? 1 : 0; | ||
| 236 | led_config += led_status.led_2 ? 2 : 0; | ||
| 237 | led_config += led_status.led_3 ? 4 : 0; | ||
| 238 | led_config += led_status.led_4 ? 8 : 0; | ||
| 239 | |||
| 240 | return static_cast<Common::Input::DriverResult>( | ||
| 241 | handle->SetLedConfig(static_cast<u8>(led_config))); | ||
| 242 | } | ||
| 243 | |||
| 244 | Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier, | ||
| 245 | Common::Input::CameraFormat camera_format) { | ||
| 246 | auto handle = GetHandle(identifier); | ||
| 247 | if (handle == nullptr) { | ||
| 248 | return Common::Input::DriverResult::InvalidHandle; | ||
| 249 | } | ||
| 250 | return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig( | ||
| 251 | Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format))); | ||
| 252 | }; | ||
| 253 | |||
| 254 | Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const { | ||
| 255 | return Common::Input::NfcState::Success; | ||
| 256 | }; | ||
| 257 | |||
| 258 | Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_, | ||
| 259 | const std::vector<u8>& data) { | ||
| 260 | return Common::Input::NfcState::NotSupported; | ||
| 261 | }; | ||
| 262 | |||
| 263 | Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, | ||
| 264 | const Common::Input::PollingMode polling_mode) { | ||
| 265 | auto handle = GetHandle(identifier); | ||
| 266 | if (handle == nullptr) { | ||
| 267 | LOG_ERROR(Input, "Invalid handle {}", identifier.port); | ||
| 268 | return Common::Input::DriverResult::InvalidHandle; | ||
| 269 | } | ||
| 270 | |||
| 271 | switch (polling_mode) { | ||
| 272 | case Common::Input::PollingMode::Active: | ||
| 273 | return static_cast<Common::Input::DriverResult>(handle->SetActiveMode()); | ||
| 274 | case Common::Input::PollingMode::Pasive: | ||
| 275 | return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode()); | ||
| 276 | case Common::Input::PollingMode::IR: | ||
| 277 | return static_cast<Common::Input::DriverResult>(handle->SetIrMode()); | ||
| 278 | case Common::Input::PollingMode::NFC: | ||
| 279 | return static_cast<Common::Input::DriverResult>(handle->SetNfcMode()); | ||
| 280 | case Common::Input::PollingMode::Ring: | ||
| 281 | return static_cast<Common::Input::DriverResult>(handle->SetRingConMode()); | ||
| 282 | default: | ||
| 283 | return Common::Input::DriverResult::NotSupported; | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, | ||
| 288 | Joycon::Battery value) { | ||
| 289 | const auto identifier = GetIdentifier(port, type); | ||
| 290 | if (value.charging != 0) { | ||
| 291 | SetBattery(identifier, Common::Input::BatteryLevel::Charging); | ||
| 292 | return; | ||
| 293 | } | ||
| 294 | |||
| 295 | Common::Input::BatteryLevel battery{}; | ||
| 296 | switch (value.status) { | ||
| 297 | case 0: | ||
| 298 | battery = Common::Input::BatteryLevel::Empty; | ||
| 299 | break; | ||
| 300 | case 1: | ||
| 301 | battery = Common::Input::BatteryLevel::Critical; | ||
| 302 | break; | ||
| 303 | case 2: | ||
| 304 | battery = Common::Input::BatteryLevel::Low; | ||
| 305 | break; | ||
| 306 | case 3: | ||
| 307 | battery = Common::Input::BatteryLevel::Medium; | ||
| 308 | break; | ||
| 309 | case 4: | ||
| 310 | default: | ||
| 311 | battery = Common::Input::BatteryLevel::Full; | ||
| 312 | break; | ||
| 313 | } | ||
| 314 | SetBattery(identifier, battery); | ||
| 315 | } | ||
| 316 | |||
| 317 | void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type, | ||
| 318 | const Joycon::Color& value) { | ||
| 319 | const auto identifier = GetIdentifier(port, type); | ||
| 320 | Common::Input::BodyColorStatus color{ | ||
| 321 | .body = value.body, | ||
| 322 | .buttons = value.buttons, | ||
| 323 | .left_grip = value.left_grip, | ||
| 324 | .right_grip = value.right_grip, | ||
| 325 | }; | ||
| 326 | SetColor(identifier, color); | ||
| 327 | } | ||
| 328 | |||
| 329 | void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) { | ||
| 330 | const auto identifier = GetIdentifier(port, type); | ||
| 331 | SetButton(identifier, id, value); | ||
| 332 | } | ||
| 333 | |||
| 334 | void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) { | ||
| 335 | const auto identifier = GetIdentifier(port, type); | ||
| 336 | SetAxis(identifier, id, value); | ||
| 337 | } | ||
| 338 | |||
| 339 | void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id, | ||
| 340 | const Joycon::MotionData& value) { | ||
| 341 | const auto identifier = GetIdentifier(port, type); | ||
| 342 | BasicMotion motion_data{ | ||
| 343 | .gyro_x = value.gyro_x, | ||
| 344 | .gyro_y = value.gyro_y, | ||
| 345 | .gyro_z = value.gyro_z, | ||
| 346 | .accel_x = value.accel_x, | ||
| 347 | .accel_y = value.accel_y, | ||
| 348 | .accel_z = value.accel_z, | ||
| 349 | .delta_timestamp = 15000, | ||
| 350 | }; | ||
| 351 | SetMotion(identifier, id, motion_data); | ||
| 352 | } | ||
| 353 | |||
| 354 | void Joycons::OnRingConUpdate(f32 ring_data) { | ||
| 355 | // To simplify ring detection it will always be mapped to an empty identifier for all | ||
| 356 | // controllers | ||
| 357 | constexpr PadIdentifier identifier = { | ||
| 358 | .guid = Common::UUID{}, | ||
| 359 | .port = 0, | ||
| 360 | .pad = 0, | ||
| 361 | }; | ||
| 362 | SetAxis(identifier, 100, ring_data); | ||
| 363 | } | ||
| 364 | |||
| 365 | void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) { | ||
| 366 | const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right); | ||
| 367 | const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved | ||
| 368 | : Common::Input::NfcState::NewAmiibo; | ||
| 369 | SetNfc(identifier, {nfc_state, amiibo_data}); | ||
| 370 | } | ||
| 371 | |||
| 372 | void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, | ||
| 373 | Joycon::IrsResolution format) { | ||
| 374 | const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right); | ||
| 375 | SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data}); | ||
| 376 | } | ||
| 377 | |||
| 378 | std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const { | ||
| 379 | auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) { | ||
| 380 | if (!device) { | ||
| 381 | return false; | ||
| 382 | } | ||
| 383 | if (!device->IsConnected()) { | ||
| 384 | return false; | ||
| 385 | } | ||
| 386 | if (device->GetDevicePort() == identifier.port) { | ||
| 387 | return true; | ||
| 388 | } | ||
| 389 | return false; | ||
| 390 | }; | ||
| 391 | const auto type = static_cast<Joycon::ControllerType>(identifier.pad); | ||
| 392 | |||
| 393 | if (type == Joycon::ControllerType::Left) { | ||
| 394 | const auto matching_device = std::ranges::find_if( | ||
| 395 | left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); }); | ||
| 396 | |||
| 397 | if (matching_device != left_joycons.end()) { | ||
| 398 | return *matching_device; | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | if (type == Joycon::ControllerType::Right) { | ||
| 403 | const auto matching_device = std::ranges::find_if( | ||
| 404 | right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); }); | ||
| 405 | |||
| 406 | if (matching_device != right_joycons.end()) { | ||
| 407 | return *matching_device; | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | return nullptr; | ||
| 412 | } | ||
| 413 | |||
| 414 | PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const { | ||
| 415 | const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0, | ||
| 416 | 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)}; | ||
| 417 | return { | ||
| 418 | .guid = Common::UUID{guid}, | ||
| 419 | .port = port, | ||
| 420 | .pad = static_cast<std::size_t>(type), | ||
| 421 | }; | ||
| 422 | } | ||
| 423 | |||
| 424 | Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const { | ||
| 425 | const auto identifier = GetIdentifier(port, type); | ||
| 426 | return { | ||
| 427 | {"engine", GetEngineName()}, | ||
| 428 | {"guid", identifier.guid.RawString()}, | ||
| 429 | {"port", std::to_string(identifier.port)}, | ||
| 430 | {"pad", std::to_string(identifier.pad)}, | ||
| 431 | }; | ||
| 432 | } | ||
| 433 | |||
| 434 | std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { | ||
| 435 | std::vector<Common::ParamPackage> devices{}; | ||
| 436 | |||
| 437 | auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) { | ||
| 438 | if (!device) { | ||
| 439 | return; | ||
| 440 | } | ||
| 441 | if (!device->IsConnected()) { | ||
| 442 | return; | ||
| 443 | } | ||
| 444 | auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType()); | ||
| 445 | std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()), | ||
| 446 | device->GetDevicePort() + 1); | ||
| 447 | param.Set("display", std::move(name)); | ||
| 448 | devices.emplace_back(param); | ||
| 449 | }; | ||
| 450 | |||
| 451 | for (const auto& controller : left_joycons) { | ||
| 452 | add_entry(controller); | ||
| 453 | } | ||
| 454 | for (const auto& controller : right_joycons) { | ||
| 455 | add_entry(controller); | ||
| 456 | } | ||
| 457 | |||
| 458 | // List dual joycon pairs | ||
| 459 | for (std::size_t i = 0; i < MaxSupportedControllers; i++) { | ||
| 460 | if (!left_joycons[i] || !right_joycons[i]) { | ||
| 461 | continue; | ||
| 462 | } | ||
| 463 | if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) { | ||
| 464 | continue; | ||
| 465 | } | ||
| 466 | auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType()); | ||
| 467 | const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType()); | ||
| 468 | const auto type = Joycon::ControllerType::Dual; | ||
| 469 | std::string name = fmt::format("{} {}", JoyconName(type), i + 1); | ||
| 470 | |||
| 471 | main_param.Set("display", std::move(name)); | ||
| 472 | main_param.Set("guid2", second_param.Get("guid", "")); | ||
| 473 | main_param.Set("pad", std::to_string(static_cast<size_t>(type))); | ||
| 474 | devices.emplace_back(main_param); | ||
| 475 | } | ||
| 476 | |||
| 477 | return devices; | ||
| 478 | } | ||
| 479 | |||
| 480 | ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) { | ||
| 481 | static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>, | ||
| 482 | 18> | ||
| 483 | switch_to_joycon_button = { | ||
| 484 | std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true}, | ||
| 485 | {Settings::NativeButton::B, Joycon::PadButton::B, true}, | ||
| 486 | {Settings::NativeButton::X, Joycon::PadButton::X, true}, | ||
| 487 | {Settings::NativeButton::Y, Joycon::PadButton::Y, true}, | ||
| 488 | {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false}, | ||
| 489 | {Settings::NativeButton::DUp, Joycon::PadButton::Up, false}, | ||
| 490 | {Settings::NativeButton::DRight, Joycon::PadButton::Right, false}, | ||
| 491 | {Settings::NativeButton::DDown, Joycon::PadButton::Down, false}, | ||
| 492 | {Settings::NativeButton::L, Joycon::PadButton::L, false}, | ||
| 493 | {Settings::NativeButton::R, Joycon::PadButton::R, true}, | ||
| 494 | {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false}, | ||
| 495 | {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true}, | ||
| 496 | {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true}, | ||
| 497 | {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false}, | ||
| 498 | {Settings::NativeButton::Home, Joycon::PadButton::Home, true}, | ||
| 499 | {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false}, | ||
| 500 | {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false}, | ||
| 501 | {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true}, | ||
| 502 | }; | ||
| 503 | |||
| 504 | if (!params.Has("port")) { | ||
| 505 | return {}; | ||
| 506 | } | ||
| 507 | |||
| 508 | ButtonMapping mapping{}; | ||
| 509 | for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) { | ||
| 510 | const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); | ||
| 511 | auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0)); | ||
| 512 | if (pad == Joycon::ControllerType::Dual) { | ||
| 513 | pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left; | ||
| 514 | } | ||
| 515 | |||
| 516 | Common::ParamPackage button_params = GetParamPackage(port, pad); | ||
| 517 | button_params.Set("button", static_cast<int>(joycon_button)); | ||
| 518 | mapping.insert_or_assign(switch_button, std::move(button_params)); | ||
| 519 | } | ||
| 520 | |||
| 521 | // Map SL and SR buttons for left joycons | ||
| 522 | if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) { | ||
| 523 | const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); | ||
| 524 | Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left); | ||
| 525 | |||
| 526 | Common::ParamPackage sl_button_params = button_params; | ||
| 527 | Common::ParamPackage sr_button_params = button_params; | ||
| 528 | sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL)); | ||
| 529 | sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR)); | ||
| 530 | mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); | ||
| 531 | mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); | ||
| 532 | } | ||
| 533 | |||
| 534 | // Map SL and SR buttons for right joycons | ||
| 535 | if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) { | ||
| 536 | const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); | ||
| 537 | Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right); | ||
| 538 | |||
| 539 | Common::ParamPackage sl_button_params = button_params; | ||
| 540 | Common::ParamPackage sr_button_params = button_params; | ||
| 541 | sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL)); | ||
| 542 | sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR)); | ||
| 543 | mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); | ||
| 544 | mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); | ||
| 545 | } | ||
| 546 | |||
| 547 | return mapping; | ||
| 548 | } | ||
| 549 | |||
| 550 | AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) { | ||
| 551 | if (!params.Has("port")) { | ||
| 552 | return {}; | ||
| 553 | } | ||
| 554 | |||
| 555 | const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); | ||
| 556 | auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0)); | ||
| 557 | auto pad_right = pad_left; | ||
| 558 | if (pad_left == Joycon::ControllerType::Dual) { | ||
| 559 | pad_left = Joycon::ControllerType::Left; | ||
| 560 | pad_right = Joycon::ControllerType::Right; | ||
| 561 | } | ||
| 562 | |||
| 563 | AnalogMapping mapping = {}; | ||
| 564 | Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left); | ||
| 565 | left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX)); | ||
| 566 | left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY)); | ||
| 567 | mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); | ||
| 568 | Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right); | ||
| 569 | right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX)); | ||
| 570 | right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY)); | ||
| 571 | mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params)); | ||
| 572 | return mapping; | ||
| 573 | } | ||
| 574 | |||
| 575 | MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) { | ||
| 576 | if (!params.Has("port")) { | ||
| 577 | return {}; | ||
| 578 | } | ||
| 579 | |||
| 580 | const std::size_t port = static_cast<std::size_t>(params.Get("port", 0)); | ||
| 581 | auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0)); | ||
| 582 | auto pad_right = pad_left; | ||
| 583 | if (pad_left == Joycon::ControllerType::Dual) { | ||
| 584 | pad_left = Joycon::ControllerType::Left; | ||
| 585 | pad_right = Joycon::ControllerType::Right; | ||
| 586 | } | ||
| 587 | |||
| 588 | MotionMapping mapping = {}; | ||
| 589 | Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left); | ||
| 590 | left_motion_params.Set("motion", 0); | ||
| 591 | mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params)); | ||
| 592 | Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right); | ||
| 593 | right_Motion_params.Set("motion", 1); | ||
| 594 | mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params)); | ||
| 595 | return mapping; | ||
| 596 | } | ||
| 597 | |||
| 598 | Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const { | ||
| 599 | const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0)); | ||
| 600 | switch (button) { | ||
| 601 | case Joycon::PadButton::Left: | ||
| 602 | return Common::Input::ButtonNames::ButtonLeft; | ||
| 603 | case Joycon::PadButton::Right: | ||
| 604 | return Common::Input::ButtonNames::ButtonRight; | ||
| 605 | case Joycon::PadButton::Down: | ||
| 606 | return Common::Input::ButtonNames::ButtonDown; | ||
| 607 | case Joycon::PadButton::Up: | ||
| 608 | return Common::Input::ButtonNames::ButtonUp; | ||
| 609 | case Joycon::PadButton::LeftSL: | ||
| 610 | case Joycon::PadButton::RightSL: | ||
| 611 | return Common::Input::ButtonNames::TriggerSL; | ||
| 612 | case Joycon::PadButton::LeftSR: | ||
| 613 | case Joycon::PadButton::RightSR: | ||
| 614 | return Common::Input::ButtonNames::TriggerSR; | ||
| 615 | case Joycon::PadButton::L: | ||
| 616 | return Common::Input::ButtonNames::TriggerL; | ||
| 617 | case Joycon::PadButton::R: | ||
| 618 | return Common::Input::ButtonNames::TriggerR; | ||
| 619 | case Joycon::PadButton::ZL: | ||
| 620 | return Common::Input::ButtonNames::TriggerZL; | ||
| 621 | case Joycon::PadButton::ZR: | ||
| 622 | return Common::Input::ButtonNames::TriggerZR; | ||
| 623 | case Joycon::PadButton::A: | ||
| 624 | return Common::Input::ButtonNames::ButtonA; | ||
| 625 | case Joycon::PadButton::B: | ||
| 626 | return Common::Input::ButtonNames::ButtonB; | ||
| 627 | case Joycon::PadButton::X: | ||
| 628 | return Common::Input::ButtonNames::ButtonX; | ||
| 629 | case Joycon::PadButton::Y: | ||
| 630 | return Common::Input::ButtonNames::ButtonY; | ||
| 631 | case Joycon::PadButton::Plus: | ||
| 632 | return Common::Input::ButtonNames::ButtonPlus; | ||
| 633 | case Joycon::PadButton::Minus: | ||
| 634 | return Common::Input::ButtonNames::ButtonMinus; | ||
| 635 | case Joycon::PadButton::Home: | ||
| 636 | return Common::Input::ButtonNames::ButtonHome; | ||
| 637 | case Joycon::PadButton::Capture: | ||
| 638 | return Common::Input::ButtonNames::ButtonCapture; | ||
| 639 | case Joycon::PadButton::StickL: | ||
| 640 | return Common::Input::ButtonNames::ButtonStickL; | ||
| 641 | case Joycon::PadButton::StickR: | ||
| 642 | return Common::Input::ButtonNames::ButtonStickR; | ||
| 643 | default: | ||
| 644 | return Common::Input::ButtonNames::Undefined; | ||
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 | Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const { | ||
| 649 | if (params.Has("button")) { | ||
| 650 | return GetUIButtonName(params); | ||
| 651 | } | ||
| 652 | if (params.Has("axis")) { | ||
| 653 | return Common::Input::ButtonNames::Value; | ||
| 654 | } | ||
| 655 | if (params.Has("motion")) { | ||
| 656 | return Common::Input::ButtonNames::Engine; | ||
| 657 | } | ||
| 658 | |||
| 659 | return Common::Input::ButtonNames::Invalid; | ||
| 660 | } | ||
| 661 | |||
| 662 | std::string Joycons::JoyconName(Joycon::ControllerType type) const { | ||
| 663 | switch (type) { | ||
| 664 | case Joycon::ControllerType::Left: | ||
| 665 | return "Left Joycon"; | ||
| 666 | case Joycon::ControllerType::Right: | ||
| 667 | return "Right Joycon"; | ||
| 668 | case Joycon::ControllerType::Pro: | ||
| 669 | return "Pro Controller"; | ||
| 670 | case Joycon::ControllerType::Grip: | ||
| 671 | return "Grip Controller"; | ||
| 672 | case Joycon::ControllerType::Dual: | ||
| 673 | return "Dual Joycon"; | ||
| 674 | default: | ||
| 675 | return "Unknown Joycon"; | ||
| 676 | } | ||
| 677 | } | ||
| 678 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h new file mode 100644 index 000000000..316d383d8 --- /dev/null +++ b/src/input_common/drivers/joycon.h | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <span> | ||
| 8 | #include <thread> | ||
| 9 | #include <SDL_hidapi.h> | ||
| 10 | |||
| 11 | #include "input_common/input_engine.h" | ||
| 12 | |||
| 13 | namespace InputCommon::Joycon { | ||
| 14 | using SerialNumber = std::array<u8, 15>; | ||
| 15 | struct Battery; | ||
| 16 | struct Color; | ||
| 17 | struct MotionData; | ||
| 18 | enum class ControllerType; | ||
| 19 | enum class DriverResult; | ||
| 20 | enum class IrsResolution; | ||
| 21 | class JoyconDriver; | ||
| 22 | } // namespace InputCommon::Joycon | ||
| 23 | |||
| 24 | namespace InputCommon { | ||
| 25 | |||
| 26 | class Joycons final : public InputCommon::InputEngine { | ||
| 27 | public: | ||
| 28 | explicit Joycons(const std::string& input_engine_); | ||
| 29 | |||
| 30 | ~Joycons(); | ||
| 31 | |||
| 32 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; | ||
| 33 | Common::Input::DriverResult SetVibration( | ||
| 34 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | ||
| 35 | |||
| 36 | Common::Input::DriverResult SetLeds(const PadIdentifier& identifier, | ||
| 37 | const Common::Input::LedStatus& led_status) override; | ||
| 38 | |||
| 39 | Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier, | ||
| 40 | Common::Input::CameraFormat camera_format) override; | ||
| 41 | |||
| 42 | Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; | ||
| 43 | Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, | ||
| 44 | const std::vector<u8>& data) override; | ||
| 45 | |||
| 46 | Common::Input::DriverResult SetPollingMode( | ||
| 47 | const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; | ||
| 48 | |||
| 49 | /// Used for automapping features | ||
| 50 | std::vector<Common::ParamPackage> GetInputDevices() const override; | ||
| 51 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; | ||
| 52 | AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; | ||
| 53 | MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override; | ||
| 54 | Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; | ||
| 55 | |||
| 56 | private: | ||
| 57 | static constexpr std::size_t MaxSupportedControllers = 8; | ||
| 58 | |||
| 59 | /// For shutting down, clear all data, join all threads, release usb devices | ||
| 60 | void Reset(); | ||
| 61 | |||
| 62 | /// Registers controllers, clears all data and starts the scan thread | ||
| 63 | void Setup(); | ||
| 64 | |||
| 65 | /// Actively searchs for new devices | ||
| 66 | void ScanThread(std::stop_token stop_token); | ||
| 67 | |||
| 68 | /// Returns true if device is valid and not registered | ||
| 69 | bool IsDeviceNew(SDL_hid_device_info* device_info) const; | ||
| 70 | |||
| 71 | /// Tries to connect to the new device | ||
| 72 | void RegisterNewDevice(SDL_hid_device_info* device_info); | ||
| 73 | |||
| 74 | /// Returns the next free handle | ||
| 75 | std::shared_ptr<Joycon::JoyconDriver> GetNextFreeHandle(Joycon::ControllerType type) const; | ||
| 76 | |||
| 77 | void OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, Joycon::Battery value); | ||
| 78 | void OnColorUpdate(std::size_t port, Joycon::ControllerType type, const Joycon::Color& value); | ||
| 79 | void OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value); | ||
| 80 | void OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value); | ||
| 81 | void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id, | ||
| 82 | const Joycon::MotionData& value); | ||
| 83 | void OnRingConUpdate(f32 ring_data); | ||
| 84 | void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data); | ||
| 85 | void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, | ||
| 86 | Joycon::IrsResolution format); | ||
| 87 | |||
| 88 | /// Returns a JoyconHandle corresponding to a PadIdentifier | ||
| 89 | std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const; | ||
| 90 | |||
| 91 | /// Returns a PadIdentifier corresponding to the port number and joycon type | ||
| 92 | PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const; | ||
| 93 | |||
| 94 | /// Returns a ParamPackage corresponding to the port number and joycon type | ||
| 95 | Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const; | ||
| 96 | |||
| 97 | std::string JoyconName(std::size_t port) const; | ||
| 98 | |||
| 99 | Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; | ||
| 100 | |||
| 101 | /// Returns the name of the device in text format | ||
| 102 | std::string JoyconName(Joycon::ControllerType type) const; | ||
| 103 | |||
| 104 | std::jthread scan_thread; | ||
| 105 | |||
| 106 | // Joycon types are split by type to ease supporting dualjoycon configurations | ||
| 107 | std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{}; | ||
| 108 | std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{}; | ||
| 109 | }; | ||
| 110 | |||
| 111 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 9835d99d2..d975eb815 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -334,6 +334,15 @@ void SDLDriver::InitJoystick(int joystick_index) { | |||
| 334 | 334 | ||
| 335 | const auto guid = GetGUID(sdl_joystick); | 335 | const auto guid = GetGUID(sdl_joystick); |
| 336 | 336 | ||
| 337 | if (Settings::values.enable_joycon_driver) { | ||
| 338 | if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && | ||
| 339 | (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) { | ||
| 340 | LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index); | ||
| 341 | SDL_JoystickClose(sdl_joystick); | ||
| 342 | return; | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 337 | std::scoped_lock lock{joystick_map_mutex}; | 346 | std::scoped_lock lock{joystick_map_mutex}; |
| 338 | if (joystick_map.find(guid) == joystick_map.end()) { | 347 | if (joystick_map.find(guid) == joystick_map.end()) { |
| 339 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); | 348 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); |
| @@ -456,9 +465,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en | |||
| 456 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); | 465 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); |
| 457 | SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); | 466 | SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); |
| 458 | 467 | ||
| 459 | // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and | 468 | // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled |
| 460 | // not a generic one | 469 | if (Settings::values.enable_joycon_driver) { |
| 461 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); | 470 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); |
| 471 | } else { | ||
| 472 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); | ||
| 473 | } | ||
| 474 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); | ||
| 462 | 475 | ||
| 463 | // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native | 476 | // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native |
| 464 | // driver on Linux. | 477 | // driver on Linux. |
| @@ -548,7 +561,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const { | |||
| 548 | return devices; | 561 | return devices; |
| 549 | } | 562 | } |
| 550 | 563 | ||
| 551 | Common::Input::VibrationError SDLDriver::SetVibration( | 564 | Common::Input::DriverResult SDLDriver::SetVibration( |
| 552 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { | 565 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { |
| 553 | const auto joystick = | 566 | const auto joystick = |
| 554 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); | 567 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); |
| @@ -582,7 +595,7 @@ Common::Input::VibrationError SDLDriver::SetVibration( | |||
| 582 | .vibration = new_vibration, | 595 | .vibration = new_vibration, |
| 583 | }); | 596 | }); |
| 584 | 597 | ||
| 585 | return Common::Input::VibrationError::None; | 598 | return Common::Input::DriverResult::Success; |
| 586 | } | 599 | } |
| 587 | 600 | ||
| 588 | bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { | 601 | bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { |
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index 366bcc496..ffde169b3 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h | |||
| @@ -63,7 +63,7 @@ public: | |||
| 63 | 63 | ||
| 64 | bool IsStickInverted(const Common::ParamPackage& params) override; | 64 | bool IsStickInverted(const Common::ParamPackage& params) override; |
| 65 | 65 | ||
| 66 | Common::Input::VibrationError SetVibration( | 66 | Common::Input::DriverResult SetVibration( |
| 67 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | 67 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; |
| 68 | 68 | ||
| 69 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; | 69 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; |
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index 63ffaca67..4a0268a4d 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp | |||
| @@ -22,22 +22,23 @@ VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move( | |||
| 22 | 22 | ||
| 23 | VirtualAmiibo::~VirtualAmiibo() = default; | 23 | VirtualAmiibo::~VirtualAmiibo() = default; |
| 24 | 24 | ||
| 25 | Common::Input::PollingError VirtualAmiibo::SetPollingMode( | 25 | Common::Input::DriverResult VirtualAmiibo::SetPollingMode( |
| 26 | [[maybe_unused]] const PadIdentifier& identifier_, | 26 | [[maybe_unused]] const PadIdentifier& identifier_, |
| 27 | const Common::Input::PollingMode polling_mode_) { | 27 | const Common::Input::PollingMode polling_mode_) { |
| 28 | polling_mode = polling_mode_; | 28 | polling_mode = polling_mode_; |
| 29 | 29 | ||
| 30 | if (polling_mode == Common::Input::PollingMode::NFC) { | 30 | switch (polling_mode) { |
| 31 | case Common::Input::PollingMode::NFC: | ||
| 31 | if (state == State::Initialized) { | 32 | if (state == State::Initialized) { |
| 32 | state = State::WaitingForAmiibo; | 33 | state = State::WaitingForAmiibo; |
| 33 | } | 34 | } |
| 34 | } else { | 35 | return Common::Input::DriverResult::Success; |
| 36 | default: | ||
| 35 | if (state == State::AmiiboIsOpen) { | 37 | if (state == State::AmiiboIsOpen) { |
| 36 | CloseAmiibo(); | 38 | CloseAmiibo(); |
| 37 | } | 39 | } |
| 40 | return Common::Input::DriverResult::NotSupported; | ||
| 38 | } | 41 | } |
| 39 | |||
| 40 | return Common::Input::PollingError::None; | ||
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | Common::Input::NfcState VirtualAmiibo::SupportsNfc( | 44 | Common::Input::NfcState VirtualAmiibo::SupportsNfc( |
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 0f9dad333..13cacfc0a 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h | |||
| @@ -36,7 +36,7 @@ public: | |||
| 36 | ~VirtualAmiibo() override; | 36 | ~VirtualAmiibo() override; |
| 37 | 37 | ||
| 38 | // Sets polling mode to a controller | 38 | // Sets polling mode to a controller |
| 39 | Common::Input::PollingError SetPollingMode( | 39 | Common::Input::DriverResult SetPollingMode( |
| 40 | const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; | 40 | const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; |
| 41 | 41 | ||
| 42 | Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; | 42 | Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; |
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp new file mode 100644 index 000000000..3775e2d35 --- /dev/null +++ b/src/input_common/helpers/joycon_driver.cpp | |||
| @@ -0,0 +1,575 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "common/swap.h" | ||
| 6 | #include "common/thread.h" | ||
| 7 | #include "input_common/helpers/joycon_driver.h" | ||
| 8 | #include "input_common/helpers/joycon_protocol/calibration.h" | ||
| 9 | #include "input_common/helpers/joycon_protocol/generic_functions.h" | ||
| 10 | #include "input_common/helpers/joycon_protocol/irs.h" | ||
| 11 | #include "input_common/helpers/joycon_protocol/nfc.h" | ||
| 12 | #include "input_common/helpers/joycon_protocol/poller.h" | ||
| 13 | #include "input_common/helpers/joycon_protocol/ringcon.h" | ||
| 14 | #include "input_common/helpers/joycon_protocol/rumble.h" | ||
| 15 | |||
| 16 | namespace InputCommon::Joycon { | ||
| 17 | JoyconDriver::JoyconDriver(std::size_t port_) : port{port_} { | ||
| 18 | hidapi_handle = std::make_shared<JoyconHandle>(); | ||
| 19 | } | ||
| 20 | |||
| 21 | JoyconDriver::~JoyconDriver() { | ||
| 22 | Stop(); | ||
| 23 | } | ||
| 24 | |||
| 25 | void JoyconDriver::Stop() { | ||
| 26 | is_connected = false; | ||
| 27 | input_thread = {}; | ||
| 28 | } | ||
| 29 | |||
| 30 | DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) { | ||
| 31 | std::scoped_lock lock{mutex}; | ||
| 32 | |||
| 33 | handle_device_type = ControllerType::None; | ||
| 34 | GetDeviceType(device_info, handle_device_type); | ||
| 35 | if (handle_device_type == ControllerType::None) { | ||
| 36 | return DriverResult::UnsupportedControllerType; | ||
| 37 | } | ||
| 38 | |||
| 39 | hidapi_handle->handle = | ||
| 40 | SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number); | ||
| 41 | std::memcpy(&handle_serial_number, device_info->serial_number, 15); | ||
| 42 | if (!hidapi_handle->handle) { | ||
| 43 | LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.", | ||
| 44 | device_info->vendor_id, device_info->product_id); | ||
| 45 | return DriverResult::HandleInUse; | ||
| 46 | } | ||
| 47 | SDL_hid_set_nonblocking(hidapi_handle->handle, 1); | ||
| 48 | return DriverResult::Success; | ||
| 49 | } | ||
| 50 | |||
| 51 | DriverResult JoyconDriver::InitializeDevice() { | ||
| 52 | if (!hidapi_handle->handle) { | ||
| 53 | return DriverResult::InvalidHandle; | ||
| 54 | } | ||
| 55 | std::scoped_lock lock{mutex}; | ||
| 56 | disable_input_thread = true; | ||
| 57 | |||
| 58 | // Reset Counters | ||
| 59 | error_counter = 0; | ||
| 60 | hidapi_handle->packet_counter = 0; | ||
| 61 | |||
| 62 | // Reset external device status | ||
| 63 | starlink_connected = false; | ||
| 64 | ring_connected = false; | ||
| 65 | amiibo_detected = false; | ||
| 66 | |||
| 67 | // Set HW default configuration | ||
| 68 | vibration_enabled = true; | ||
| 69 | motion_enabled = true; | ||
| 70 | hidbus_enabled = false; | ||
| 71 | nfc_enabled = false; | ||
| 72 | passive_enabled = false; | ||
| 73 | irs_enabled = false; | ||
| 74 | gyro_sensitivity = Joycon::GyroSensitivity::DPS2000; | ||
| 75 | gyro_performance = Joycon::GyroPerformance::HZ833; | ||
| 76 | accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8; | ||
| 77 | accelerometer_performance = Joycon::AccelerometerPerformance::HZ100; | ||
| 78 | |||
| 79 | // Initialize HW Protocols | ||
| 80 | calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle); | ||
| 81 | generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle); | ||
| 82 | irs_protocol = std::make_unique<IrsProtocol>(hidapi_handle); | ||
| 83 | nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle); | ||
| 84 | ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle); | ||
| 85 | rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); | ||
| 86 | |||
| 87 | // Get fixed joycon info | ||
| 88 | generic_protocol->GetVersionNumber(version); | ||
| 89 | generic_protocol->SetLowPowerMode(false); | ||
| 90 | generic_protocol->GetColor(color); | ||
| 91 | if (handle_device_type == ControllerType::Pro) { | ||
| 92 | // Some 3rd party controllers aren't pro controllers | ||
| 93 | generic_protocol->GetControllerType(device_type); | ||
| 94 | } else { | ||
| 95 | device_type = handle_device_type; | ||
| 96 | } | ||
| 97 | generic_protocol->GetSerialNumber(serial_number); | ||
| 98 | supported_features = GetSupportedFeatures(); | ||
| 99 | |||
| 100 | // Get Calibration data | ||
| 101 | calibration_protocol->GetLeftJoyStickCalibration(left_stick_calibration); | ||
| 102 | calibration_protocol->GetRightJoyStickCalibration(right_stick_calibration); | ||
| 103 | calibration_protocol->GetImuCalibration(motion_calibration); | ||
| 104 | |||
| 105 | // Set led status | ||
| 106 | generic_protocol->SetLedBlinkPattern(static_cast<u8>(1 + port)); | ||
| 107 | |||
| 108 | // Apply HW configuration | ||
| 109 | SetPollingMode(); | ||
| 110 | |||
| 111 | // Initialize joycon poller | ||
| 112 | joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration, | ||
| 113 | right_stick_calibration, motion_calibration); | ||
| 114 | |||
| 115 | // Start pooling for data | ||
| 116 | is_connected = true; | ||
| 117 | if (!input_thread_running) { | ||
| 118 | input_thread = | ||
| 119 | std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); }); | ||
| 120 | } | ||
| 121 | |||
| 122 | disable_input_thread = false; | ||
| 123 | return DriverResult::Success; | ||
| 124 | } | ||
| 125 | |||
| 126 | void JoyconDriver::InputThread(std::stop_token stop_token) { | ||
| 127 | LOG_INFO(Input, "Joycon Adapter input thread started"); | ||
| 128 | Common::SetCurrentThreadName("JoyconInput"); | ||
| 129 | input_thread_running = true; | ||
| 130 | |||
| 131 | // Max update rate is 5ms, ensure we are always able to read a bit faster | ||
| 132 | constexpr int ThreadDelay = 2; | ||
| 133 | std::vector<u8> buffer(MaxBufferSize); | ||
| 134 | |||
| 135 | while (!stop_token.stop_requested()) { | ||
| 136 | int status = 0; | ||
| 137 | |||
| 138 | if (!IsInputThreadValid()) { | ||
| 139 | input_thread.request_stop(); | ||
| 140 | continue; | ||
| 141 | } | ||
| 142 | |||
| 143 | // By disabling the input thread we can ensure custom commands will succeed as no package is | ||
| 144 | // skipped | ||
| 145 | if (!disable_input_thread) { | ||
| 146 | status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(), | ||
| 147 | ThreadDelay); | ||
| 148 | } else { | ||
| 149 | std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay)); | ||
| 150 | } | ||
| 151 | |||
| 152 | if (IsPayloadCorrect(status, buffer)) { | ||
| 153 | OnNewData(buffer); | ||
| 154 | } | ||
| 155 | |||
| 156 | std::this_thread::yield(); | ||
| 157 | } | ||
| 158 | |||
| 159 | is_connected = false; | ||
| 160 | input_thread_running = false; | ||
| 161 | LOG_INFO(Input, "Joycon Adapter input thread stopped"); | ||
| 162 | } | ||
| 163 | |||
| 164 | void JoyconDriver::OnNewData(std::span<u8> buffer) { | ||
| 165 | const auto report_mode = static_cast<InputReport>(buffer[0]); | ||
| 166 | |||
| 167 | // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion | ||
| 168 | // experience | ||
| 169 | switch (report_mode) { | ||
| 170 | case InputReport::STANDARD_FULL_60HZ: | ||
| 171 | case InputReport::NFC_IR_MODE_60HZ: | ||
| 172 | case InputReport::SIMPLE_HID_MODE: { | ||
| 173 | const auto now = std::chrono::steady_clock::now(); | ||
| 174 | const auto new_delta_time = static_cast<u64>( | ||
| 175 | std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); | ||
| 176 | delta_time = ((delta_time * 8) + (new_delta_time * 2)) / 10; | ||
| 177 | last_update = now; | ||
| 178 | joycon_poller->UpdateColor(color); | ||
| 179 | break; | ||
| 180 | } | ||
| 181 | default: | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | |||
| 185 | const MotionStatus motion_status{ | ||
| 186 | .is_enabled = motion_enabled, | ||
| 187 | .delta_time = delta_time, | ||
| 188 | .gyro_sensitivity = gyro_sensitivity, | ||
| 189 | .accelerometer_sensitivity = accelerometer_sensitivity, | ||
| 190 | }; | ||
| 191 | |||
| 192 | // TODO: Remove this when calibration is properly loaded and not calculated | ||
| 193 | if (ring_connected && report_mode == InputReport::STANDARD_FULL_60HZ) { | ||
| 194 | InputReportActive data{}; | ||
| 195 | memcpy(&data, buffer.data(), sizeof(InputReportActive)); | ||
| 196 | calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input); | ||
| 197 | } | ||
| 198 | |||
| 199 | const RingStatus ring_status{ | ||
| 200 | .is_enabled = ring_connected, | ||
| 201 | .default_value = ring_calibration.default_value, | ||
| 202 | .max_value = ring_calibration.max_value, | ||
| 203 | .min_value = ring_calibration.min_value, | ||
| 204 | }; | ||
| 205 | |||
| 206 | if (irs_protocol->IsEnabled()) { | ||
| 207 | irs_protocol->RequestImage(buffer); | ||
| 208 | joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat()); | ||
| 209 | } | ||
| 210 | |||
| 211 | if (nfc_protocol->IsEnabled()) { | ||
| 212 | if (amiibo_detected) { | ||
| 213 | if (!nfc_protocol->HasAmiibo()) { | ||
| 214 | joycon_poller->UpdateAmiibo({}); | ||
| 215 | amiibo_detected = false; | ||
| 216 | return; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | if (!amiibo_detected) { | ||
| 221 | std::vector<u8> data(0x21C); | ||
| 222 | const auto result = nfc_protocol->ScanAmiibo(data); | ||
| 223 | if (result == DriverResult::Success) { | ||
| 224 | joycon_poller->UpdateAmiibo(data); | ||
| 225 | amiibo_detected = true; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | switch (report_mode) { | ||
| 231 | case InputReport::STANDARD_FULL_60HZ: | ||
| 232 | joycon_poller->ReadActiveMode(buffer, motion_status, ring_status); | ||
| 233 | break; | ||
| 234 | case InputReport::NFC_IR_MODE_60HZ: | ||
| 235 | joycon_poller->ReadNfcIRMode(buffer, motion_status); | ||
| 236 | break; | ||
| 237 | case InputReport::SIMPLE_HID_MODE: | ||
| 238 | joycon_poller->ReadPassiveMode(buffer); | ||
| 239 | break; | ||
| 240 | case InputReport::SUBCMD_REPLY: | ||
| 241 | LOG_DEBUG(Input, "Unhandled command reply"); | ||
| 242 | break; | ||
| 243 | default: | ||
| 244 | LOG_ERROR(Input, "Report mode not Implemented {}", report_mode); | ||
| 245 | break; | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | DriverResult JoyconDriver::SetPollingMode() { | ||
| 250 | disable_input_thread = true; | ||
| 251 | |||
| 252 | rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); | ||
| 253 | |||
| 254 | if (motion_enabled && supported_features.motion) { | ||
| 255 | generic_protocol->EnableImu(true); | ||
| 256 | generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance, | ||
| 257 | accelerometer_sensitivity, accelerometer_performance); | ||
| 258 | } else { | ||
| 259 | generic_protocol->EnableImu(false); | ||
| 260 | } | ||
| 261 | |||
| 262 | if (irs_protocol->IsEnabled()) { | ||
| 263 | irs_protocol->DisableIrs(); | ||
| 264 | } | ||
| 265 | |||
| 266 | if (nfc_protocol->IsEnabled()) { | ||
| 267 | amiibo_detected = false; | ||
| 268 | nfc_protocol->DisableNfc(); | ||
| 269 | } | ||
| 270 | |||
| 271 | if (ring_protocol->IsEnabled()) { | ||
| 272 | ring_connected = false; | ||
| 273 | ring_protocol->DisableRingCon(); | ||
| 274 | } | ||
| 275 | |||
| 276 | if (irs_enabled && supported_features.irs) { | ||
| 277 | auto result = irs_protocol->EnableIrs(); | ||
| 278 | if (result == DriverResult::Success) { | ||
| 279 | disable_input_thread = false; | ||
| 280 | return result; | ||
| 281 | } | ||
| 282 | irs_protocol->DisableIrs(); | ||
| 283 | LOG_ERROR(Input, "Error enabling IRS"); | ||
| 284 | } | ||
| 285 | |||
| 286 | if (nfc_enabled && supported_features.nfc) { | ||
| 287 | auto result = nfc_protocol->EnableNfc(); | ||
| 288 | if (result == DriverResult::Success) { | ||
| 289 | result = nfc_protocol->StartNFCPollingMode(); | ||
| 290 | } | ||
| 291 | if (result == DriverResult::Success) { | ||
| 292 | disable_input_thread = false; | ||
| 293 | return result; | ||
| 294 | } | ||
| 295 | nfc_protocol->DisableNfc(); | ||
| 296 | LOG_ERROR(Input, "Error enabling NFC"); | ||
| 297 | } | ||
| 298 | |||
| 299 | if (hidbus_enabled && supported_features.hidbus) { | ||
| 300 | auto result = ring_protocol->EnableRingCon(); | ||
| 301 | if (result == DriverResult::Success) { | ||
| 302 | result = ring_protocol->StartRingconPolling(); | ||
| 303 | } | ||
| 304 | if (result == DriverResult::Success) { | ||
| 305 | ring_connected = true; | ||
| 306 | disable_input_thread = false; | ||
| 307 | return result; | ||
| 308 | } | ||
| 309 | ring_connected = false; | ||
| 310 | ring_protocol->DisableRingCon(); | ||
| 311 | LOG_ERROR(Input, "Error enabling Ringcon"); | ||
| 312 | } | ||
| 313 | |||
| 314 | if (passive_enabled && supported_features.passive) { | ||
| 315 | const auto result = generic_protocol->EnablePassiveMode(); | ||
| 316 | if (result == DriverResult::Success) { | ||
| 317 | disable_input_thread = false; | ||
| 318 | return result; | ||
| 319 | } | ||
| 320 | LOG_ERROR(Input, "Error enabling passive mode"); | ||
| 321 | } | ||
| 322 | |||
| 323 | // Default Mode | ||
| 324 | const auto result = generic_protocol->EnableActiveMode(); | ||
| 325 | if (result != DriverResult::Success) { | ||
| 326 | LOG_ERROR(Input, "Error enabling active mode"); | ||
| 327 | } | ||
| 328 | // Switch calls this function after enabling active mode | ||
| 329 | generic_protocol->TriggersElapsed(); | ||
| 330 | |||
| 331 | disable_input_thread = false; | ||
| 332 | return result; | ||
| 333 | } | ||
| 334 | |||
| 335 | JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() { | ||
| 336 | SupportedFeatures features{ | ||
| 337 | .passive = true, | ||
| 338 | .motion = true, | ||
| 339 | .vibration = true, | ||
| 340 | }; | ||
| 341 | |||
| 342 | if (device_type == ControllerType::Right) { | ||
| 343 | features.nfc = true; | ||
| 344 | features.irs = true; | ||
| 345 | features.hidbus = true; | ||
| 346 | } | ||
| 347 | |||
| 348 | if (device_type == ControllerType::Pro) { | ||
| 349 | features.nfc = true; | ||
| 350 | } | ||
| 351 | return features; | ||
| 352 | } | ||
| 353 | |||
| 354 | bool JoyconDriver::IsInputThreadValid() const { | ||
| 355 | if (!is_connected.load()) { | ||
| 356 | return false; | ||
| 357 | } | ||
| 358 | if (hidapi_handle->handle == nullptr) { | ||
| 359 | return false; | ||
| 360 | } | ||
| 361 | // Controller is not responding. Terminate connection | ||
| 362 | if (error_counter > MaxErrorCount) { | ||
| 363 | return false; | ||
| 364 | } | ||
| 365 | return true; | ||
| 366 | } | ||
| 367 | |||
| 368 | bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) { | ||
| 369 | if (status <= -1) { | ||
| 370 | error_counter++; | ||
| 371 | return false; | ||
| 372 | } | ||
| 373 | // There's no new data | ||
| 374 | if (status == 0) { | ||
| 375 | return false; | ||
| 376 | } | ||
| 377 | // No reply ever starts with zero | ||
| 378 | if (buffer[0] == 0x00) { | ||
| 379 | error_counter++; | ||
| 380 | return false; | ||
| 381 | } | ||
| 382 | error_counter = 0; | ||
| 383 | return true; | ||
| 384 | } | ||
| 385 | |||
| 386 | DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) { | ||
| 387 | std::scoped_lock lock{mutex}; | ||
| 388 | if (disable_input_thread) { | ||
| 389 | return DriverResult::HandleInUse; | ||
| 390 | } | ||
| 391 | return rumble_protocol->SendVibration(vibration); | ||
| 392 | } | ||
| 393 | |||
| 394 | DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) { | ||
| 395 | std::scoped_lock lock{mutex}; | ||
| 396 | if (disable_input_thread) { | ||
| 397 | return DriverResult::HandleInUse; | ||
| 398 | } | ||
| 399 | return generic_protocol->SetLedPattern(led_pattern); | ||
| 400 | } | ||
| 401 | |||
| 402 | DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) { | ||
| 403 | std::scoped_lock lock{mutex}; | ||
| 404 | if (disable_input_thread) { | ||
| 405 | return DriverResult::HandleInUse; | ||
| 406 | } | ||
| 407 | disable_input_thread = true; | ||
| 408 | const auto result = irs_protocol->SetIrsConfig(mode_, format_); | ||
| 409 | disable_input_thread = false; | ||
| 410 | return result; | ||
| 411 | } | ||
| 412 | |||
| 413 | DriverResult JoyconDriver::SetPasiveMode() { | ||
| 414 | std::scoped_lock lock{mutex}; | ||
| 415 | motion_enabled = false; | ||
| 416 | hidbus_enabled = false; | ||
| 417 | nfc_enabled = false; | ||
| 418 | passive_enabled = true; | ||
| 419 | irs_enabled = false; | ||
| 420 | return SetPollingMode(); | ||
| 421 | } | ||
| 422 | |||
| 423 | DriverResult JoyconDriver::SetActiveMode() { | ||
| 424 | if (is_ring_disabled_by_irs) { | ||
| 425 | is_ring_disabled_by_irs = false; | ||
| 426 | SetActiveMode(); | ||
| 427 | return SetRingConMode(); | ||
| 428 | } | ||
| 429 | |||
| 430 | std::scoped_lock lock{mutex}; | ||
| 431 | motion_enabled = true; | ||
| 432 | hidbus_enabled = false; | ||
| 433 | nfc_enabled = false; | ||
| 434 | passive_enabled = false; | ||
| 435 | irs_enabled = false; | ||
| 436 | return SetPollingMode(); | ||
| 437 | } | ||
| 438 | |||
| 439 | DriverResult JoyconDriver::SetIrMode() { | ||
| 440 | std::scoped_lock lock{mutex}; | ||
| 441 | |||
| 442 | if (!supported_features.irs) { | ||
| 443 | return DriverResult::NotSupported; | ||
| 444 | } | ||
| 445 | |||
| 446 | if (ring_connected) { | ||
| 447 | is_ring_disabled_by_irs = true; | ||
| 448 | } | ||
| 449 | |||
| 450 | motion_enabled = false; | ||
| 451 | hidbus_enabled = false; | ||
| 452 | nfc_enabled = false; | ||
| 453 | passive_enabled = false; | ||
| 454 | irs_enabled = true; | ||
| 455 | return SetPollingMode(); | ||
| 456 | } | ||
| 457 | |||
| 458 | DriverResult JoyconDriver::SetNfcMode() { | ||
| 459 | std::scoped_lock lock{mutex}; | ||
| 460 | |||
| 461 | if (!supported_features.nfc) { | ||
| 462 | return DriverResult::NotSupported; | ||
| 463 | } | ||
| 464 | |||
| 465 | motion_enabled = true; | ||
| 466 | hidbus_enabled = false; | ||
| 467 | nfc_enabled = true; | ||
| 468 | passive_enabled = false; | ||
| 469 | irs_enabled = false; | ||
| 470 | return SetPollingMode(); | ||
| 471 | } | ||
| 472 | |||
| 473 | DriverResult JoyconDriver::SetRingConMode() { | ||
| 474 | std::scoped_lock lock{mutex}; | ||
| 475 | |||
| 476 | if (!supported_features.hidbus) { | ||
| 477 | return DriverResult::NotSupported; | ||
| 478 | } | ||
| 479 | |||
| 480 | motion_enabled = true; | ||
| 481 | hidbus_enabled = true; | ||
| 482 | nfc_enabled = false; | ||
| 483 | passive_enabled = false; | ||
| 484 | irs_enabled = false; | ||
| 485 | |||
| 486 | const auto result = SetPollingMode(); | ||
| 487 | |||
| 488 | if (!ring_connected) { | ||
| 489 | return DriverResult::NoDeviceDetected; | ||
| 490 | } | ||
| 491 | |||
| 492 | return result; | ||
| 493 | } | ||
| 494 | |||
| 495 | bool JoyconDriver::IsConnected() const { | ||
| 496 | std::scoped_lock lock{mutex}; | ||
| 497 | return is_connected.load(); | ||
| 498 | } | ||
| 499 | |||
| 500 | bool JoyconDriver::IsVibrationEnabled() const { | ||
| 501 | std::scoped_lock lock{mutex}; | ||
| 502 | return vibration_enabled; | ||
| 503 | } | ||
| 504 | |||
| 505 | FirmwareVersion JoyconDriver::GetDeviceVersion() const { | ||
| 506 | std::scoped_lock lock{mutex}; | ||
| 507 | return version; | ||
| 508 | } | ||
| 509 | |||
| 510 | Color JoyconDriver::GetDeviceColor() const { | ||
| 511 | std::scoped_lock lock{mutex}; | ||
| 512 | return color; | ||
| 513 | } | ||
| 514 | |||
| 515 | std::size_t JoyconDriver::GetDevicePort() const { | ||
| 516 | std::scoped_lock lock{mutex}; | ||
| 517 | return port; | ||
| 518 | } | ||
| 519 | |||
| 520 | ControllerType JoyconDriver::GetDeviceType() const { | ||
| 521 | std::scoped_lock lock{mutex}; | ||
| 522 | return device_type; | ||
| 523 | } | ||
| 524 | |||
| 525 | ControllerType JoyconDriver::GetHandleDeviceType() const { | ||
| 526 | std::scoped_lock lock{mutex}; | ||
| 527 | return handle_device_type; | ||
| 528 | } | ||
| 529 | |||
| 530 | SerialNumber JoyconDriver::GetSerialNumber() const { | ||
| 531 | std::scoped_lock lock{mutex}; | ||
| 532 | return serial_number; | ||
| 533 | } | ||
| 534 | |||
| 535 | SerialNumber JoyconDriver::GetHandleSerialNumber() const { | ||
| 536 | std::scoped_lock lock{mutex}; | ||
| 537 | return handle_serial_number; | ||
| 538 | } | ||
| 539 | |||
| 540 | void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) { | ||
| 541 | joycon_poller->SetCallbacks(callbacks); | ||
| 542 | } | ||
| 543 | |||
| 544 | DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, | ||
| 545 | ControllerType& controller_type) { | ||
| 546 | static constexpr std::array<std::pair<u32, ControllerType>, 2> supported_devices{ | ||
| 547 | std::pair<u32, ControllerType>{0x2006, ControllerType::Left}, | ||
| 548 | {0x2007, ControllerType::Right}, | ||
| 549 | }; | ||
| 550 | constexpr u16 nintendo_vendor_id = 0x057e; | ||
| 551 | |||
| 552 | controller_type = ControllerType::None; | ||
| 553 | if (device_info->vendor_id != nintendo_vendor_id) { | ||
| 554 | return DriverResult::UnsupportedControllerType; | ||
| 555 | } | ||
| 556 | |||
| 557 | for (const auto& [product_id, type] : supported_devices) { | ||
| 558 | if (device_info->product_id == static_cast<u16>(product_id)) { | ||
| 559 | controller_type = type; | ||
| 560 | return Joycon::DriverResult::Success; | ||
| 561 | } | ||
| 562 | } | ||
| 563 | return Joycon::DriverResult::UnsupportedControllerType; | ||
| 564 | } | ||
| 565 | |||
| 566 | DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info, | ||
| 567 | SerialNumber& serial_number) { | ||
| 568 | if (device_info->serial_number == nullptr) { | ||
| 569 | return DriverResult::Unknown; | ||
| 570 | } | ||
| 571 | std::memcpy(&serial_number, device_info->serial_number, 15); | ||
| 572 | return Joycon::DriverResult::Success; | ||
| 573 | } | ||
| 574 | |||
| 575 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h new file mode 100644 index 000000000..c1e189fa5 --- /dev/null +++ b/src/input_common/helpers/joycon_driver.h | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <atomic> | ||
| 7 | #include <functional> | ||
| 8 | #include <mutex> | ||
| 9 | #include <span> | ||
| 10 | #include <thread> | ||
| 11 | |||
| 12 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 13 | |||
| 14 | namespace InputCommon::Joycon { | ||
| 15 | class CalibrationProtocol; | ||
| 16 | class GenericProtocol; | ||
| 17 | class IrsProtocol; | ||
| 18 | class NfcProtocol; | ||
| 19 | class JoyconPoller; | ||
| 20 | class RingConProtocol; | ||
| 21 | class RumbleProtocol; | ||
| 22 | |||
| 23 | class JoyconDriver final { | ||
| 24 | public: | ||
| 25 | explicit JoyconDriver(std::size_t port_); | ||
| 26 | |||
| 27 | ~JoyconDriver(); | ||
| 28 | |||
| 29 | DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info); | ||
| 30 | DriverResult InitializeDevice(); | ||
| 31 | void Stop(); | ||
| 32 | |||
| 33 | bool IsConnected() const; | ||
| 34 | bool IsVibrationEnabled() const; | ||
| 35 | |||
| 36 | FirmwareVersion GetDeviceVersion() const; | ||
| 37 | Color GetDeviceColor() const; | ||
| 38 | std::size_t GetDevicePort() const; | ||
| 39 | ControllerType GetDeviceType() const; | ||
| 40 | ControllerType GetHandleDeviceType() const; | ||
| 41 | SerialNumber GetSerialNumber() const; | ||
| 42 | SerialNumber GetHandleSerialNumber() const; | ||
| 43 | |||
| 44 | DriverResult SetVibration(const VibrationValue& vibration); | ||
| 45 | DriverResult SetLedConfig(u8 led_pattern); | ||
| 46 | DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_); | ||
| 47 | DriverResult SetPasiveMode(); | ||
| 48 | DriverResult SetActiveMode(); | ||
| 49 | DriverResult SetIrMode(); | ||
| 50 | DriverResult SetNfcMode(); | ||
| 51 | DriverResult SetRingConMode(); | ||
| 52 | |||
| 53 | void SetCallbacks(const JoyconCallbacks& callbacks); | ||
| 54 | |||
| 55 | // Returns device type from hidapi handle | ||
| 56 | static DriverResult GetDeviceType(SDL_hid_device_info* device_info, | ||
| 57 | ControllerType& controller_type); | ||
| 58 | |||
| 59 | // Returns serial number from hidapi handle | ||
| 60 | static DriverResult GetSerialNumber(SDL_hid_device_info* device_info, | ||
| 61 | SerialNumber& serial_number); | ||
| 62 | |||
| 63 | private: | ||
| 64 | struct SupportedFeatures { | ||
| 65 | bool passive{}; | ||
| 66 | bool hidbus{}; | ||
| 67 | bool irs{}; | ||
| 68 | bool motion{}; | ||
| 69 | bool nfc{}; | ||
| 70 | bool vibration{}; | ||
| 71 | }; | ||
| 72 | |||
| 73 | /// Main thread, actively request new data from the handle | ||
| 74 | void InputThread(std::stop_token stop_token); | ||
| 75 | |||
| 76 | /// Called everytime a valid package arrives | ||
| 77 | void OnNewData(std::span<u8> buffer); | ||
| 78 | |||
| 79 | /// Updates device configuration to enable or disable features | ||
| 80 | DriverResult SetPollingMode(); | ||
| 81 | |||
| 82 | /// Returns true if input thread is valid and doesn't need to be stopped | ||
| 83 | bool IsInputThreadValid() const; | ||
| 84 | |||
| 85 | /// Returns true if the data should be interpreted. Otherwise the error counter is incremented | ||
| 86 | bool IsPayloadCorrect(int status, std::span<const u8> buffer); | ||
| 87 | |||
| 88 | /// Returns a list of supported features that can be enabled on this device | ||
| 89 | SupportedFeatures GetSupportedFeatures(); | ||
| 90 | |||
| 91 | // Protocol Features | ||
| 92 | std::unique_ptr<CalibrationProtocol> calibration_protocol; | ||
| 93 | std::unique_ptr<GenericProtocol> generic_protocol; | ||
| 94 | std::unique_ptr<IrsProtocol> irs_protocol; | ||
| 95 | std::unique_ptr<NfcProtocol> nfc_protocol; | ||
| 96 | std::unique_ptr<JoyconPoller> joycon_poller; | ||
| 97 | std::unique_ptr<RingConProtocol> ring_protocol; | ||
| 98 | std::unique_ptr<RumbleProtocol> rumble_protocol; | ||
| 99 | |||
| 100 | // Connection status | ||
| 101 | std::atomic<bool> is_connected{}; | ||
| 102 | u64 delta_time; | ||
| 103 | std::size_t error_counter{}; | ||
| 104 | std::shared_ptr<JoyconHandle> hidapi_handle; | ||
| 105 | std::chrono::time_point<std::chrono::steady_clock> last_update; | ||
| 106 | |||
| 107 | // External device status | ||
| 108 | bool starlink_connected{}; | ||
| 109 | bool ring_connected{}; | ||
| 110 | bool amiibo_detected{}; | ||
| 111 | bool is_ring_disabled_by_irs{}; | ||
| 112 | |||
| 113 | // Harware configuration | ||
| 114 | u8 leds{}; | ||
| 115 | ReportMode mode{}; | ||
| 116 | bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time | ||
| 117 | bool hidbus_enabled{}; // External device support | ||
| 118 | bool irs_enabled{}; // Infrared camera input | ||
| 119 | bool motion_enabled{}; // Enables motion input | ||
| 120 | bool nfc_enabled{}; // Enables Amiibo detection | ||
| 121 | bool vibration_enabled{}; // Allows vibrations | ||
| 122 | |||
| 123 | // Calibration data | ||
| 124 | GyroSensitivity gyro_sensitivity{}; | ||
| 125 | GyroPerformance gyro_performance{}; | ||
| 126 | AccelerometerSensitivity accelerometer_sensitivity{}; | ||
| 127 | AccelerometerPerformance accelerometer_performance{}; | ||
| 128 | JoyStickCalibration left_stick_calibration{}; | ||
| 129 | JoyStickCalibration right_stick_calibration{}; | ||
| 130 | MotionCalibration motion_calibration{}; | ||
| 131 | RingCalibration ring_calibration{}; | ||
| 132 | |||
| 133 | // Fixed joycon info | ||
| 134 | FirmwareVersion version{}; | ||
| 135 | Color color{}; | ||
| 136 | std::size_t port{}; | ||
| 137 | ControllerType device_type{}; // Device type reported by controller | ||
| 138 | ControllerType handle_device_type{}; // Device type reported by hidapi | ||
| 139 | SerialNumber serial_number{}; // Serial number reported by controller | ||
| 140 | SerialNumber handle_serial_number{}; // Serial number type reported by hidapi | ||
| 141 | SupportedFeatures supported_features{}; | ||
| 142 | |||
| 143 | // Thread related | ||
| 144 | mutable std::mutex mutex; | ||
| 145 | std::jthread input_thread; | ||
| 146 | bool input_thread_running{}; | ||
| 147 | bool disable_input_thread{}; | ||
| 148 | }; | ||
| 149 | |||
| 150 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp new file mode 100644 index 000000000..f6e7e97d5 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/calibration.cpp | |||
| @@ -0,0 +1,184 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <cstring> | ||
| 5 | |||
| 6 | #include "input_common/helpers/joycon_protocol/calibration.h" | ||
| 7 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 8 | |||
| 9 | namespace InputCommon::Joycon { | ||
| 10 | |||
| 11 | CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle) | ||
| 12 | : JoyconCommonProtocol(std::move(handle)) {} | ||
| 13 | |||
| 14 | DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { | ||
| 15 | ScopedSetBlocking sb(this); | ||
| 16 | std::vector<u8> buffer; | ||
| 17 | DriverResult result{DriverResult::Success}; | ||
| 18 | calibration = {}; | ||
| 19 | |||
| 20 | result = ReadSPI(CalAddr::USER_LEFT_MAGIC, sizeof(u16), buffer); | ||
| 21 | |||
| 22 | if (result == DriverResult::Success) { | ||
| 23 | const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; | ||
| 24 | if (has_user_calibration) { | ||
| 25 | result = ReadSPI(CalAddr::USER_LEFT_DATA, 9, buffer); | ||
| 26 | } else { | ||
| 27 | result = ReadSPI(CalAddr::FACT_LEFT_DATA, 9, buffer); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | if (result == DriverResult::Success) { | ||
| 32 | calibration.x.max = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); | ||
| 33 | calibration.y.max = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); | ||
| 34 | calibration.x.center = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); | ||
| 35 | calibration.y.center = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); | ||
| 36 | calibration.x.min = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); | ||
| 37 | calibration.y.min = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); | ||
| 38 | } | ||
| 39 | |||
| 40 | // Nintendo fix for drifting stick | ||
| 41 | // result = ReadSPI(0x60, 0x86 ,buffer, 16); | ||
| 42 | // calibration.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); | ||
| 43 | |||
| 44 | // Set a valid default calibration if data is missing | ||
| 45 | ValidateCalibration(calibration); | ||
| 46 | |||
| 47 | return result; | ||
| 48 | } | ||
| 49 | |||
| 50 | DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { | ||
| 51 | ScopedSetBlocking sb(this); | ||
| 52 | std::vector<u8> buffer; | ||
| 53 | DriverResult result{DriverResult::Success}; | ||
| 54 | calibration = {}; | ||
| 55 | |||
| 56 | result = ReadSPI(CalAddr::USER_RIGHT_MAGIC, sizeof(u16), buffer); | ||
| 57 | |||
| 58 | if (result == DriverResult::Success) { | ||
| 59 | const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; | ||
| 60 | if (has_user_calibration) { | ||
| 61 | result = ReadSPI(CalAddr::USER_RIGHT_DATA, 9, buffer); | ||
| 62 | } else { | ||
| 63 | result = ReadSPI(CalAddr::FACT_RIGHT_DATA, 9, buffer); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | if (result == DriverResult::Success) { | ||
| 68 | calibration.x.center = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); | ||
| 69 | calibration.y.center = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); | ||
| 70 | calibration.x.min = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); | ||
| 71 | calibration.y.min = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); | ||
| 72 | calibration.x.max = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); | ||
| 73 | calibration.y.max = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); | ||
| 74 | } | ||
| 75 | |||
| 76 | // Nintendo fix for drifting stick | ||
| 77 | // buffer = ReadSPI(0x60, 0x98 , 16); | ||
| 78 | // joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); | ||
| 79 | |||
| 80 | // Set a valid default calibration if data is missing | ||
| 81 | ValidateCalibration(calibration); | ||
| 82 | |||
| 83 | return result; | ||
| 84 | } | ||
| 85 | |||
| 86 | DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { | ||
| 87 | ScopedSetBlocking sb(this); | ||
| 88 | std::vector<u8> buffer; | ||
| 89 | DriverResult result{DriverResult::Success}; | ||
| 90 | calibration = {}; | ||
| 91 | |||
| 92 | result = ReadSPI(CalAddr::USER_IMU_MAGIC, sizeof(u16), buffer); | ||
| 93 | |||
| 94 | if (result == DriverResult::Success) { | ||
| 95 | const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; | ||
| 96 | if (has_user_calibration) { | ||
| 97 | result = ReadSPI(CalAddr::USER_IMU_DATA, sizeof(IMUCalibration), buffer); | ||
| 98 | } else { | ||
| 99 | result = ReadSPI(CalAddr::FACT_IMU_DATA, sizeof(IMUCalibration), buffer); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | if (result == DriverResult::Success) { | ||
| 104 | IMUCalibration device_calibration{}; | ||
| 105 | memcpy(&device_calibration, buffer.data(), sizeof(IMUCalibration)); | ||
| 106 | calibration.accelerometer[0].offset = device_calibration.accelerometer_offset[0]; | ||
| 107 | calibration.accelerometer[1].offset = device_calibration.accelerometer_offset[1]; | ||
| 108 | calibration.accelerometer[2].offset = device_calibration.accelerometer_offset[2]; | ||
| 109 | |||
| 110 | calibration.accelerometer[0].scale = device_calibration.accelerometer_scale[0]; | ||
| 111 | calibration.accelerometer[1].scale = device_calibration.accelerometer_scale[1]; | ||
| 112 | calibration.accelerometer[2].scale = device_calibration.accelerometer_scale[2]; | ||
| 113 | |||
| 114 | calibration.gyro[0].offset = device_calibration.gyroscope_offset[0]; | ||
| 115 | calibration.gyro[1].offset = device_calibration.gyroscope_offset[1]; | ||
| 116 | calibration.gyro[2].offset = device_calibration.gyroscope_offset[2]; | ||
| 117 | |||
| 118 | calibration.gyro[0].scale = device_calibration.gyroscope_scale[0]; | ||
| 119 | calibration.gyro[1].scale = device_calibration.gyroscope_scale[1]; | ||
| 120 | calibration.gyro[2].scale = device_calibration.gyroscope_scale[2]; | ||
| 121 | } | ||
| 122 | |||
| 123 | ValidateCalibration(calibration); | ||
| 124 | |||
| 125 | return result; | ||
| 126 | } | ||
| 127 | |||
| 128 | DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, | ||
| 129 | s16 current_value) { | ||
| 130 | // TODO: Get default calibration form ring itself | ||
| 131 | if (ring_data_max == 0 && ring_data_min == 0) { | ||
| 132 | ring_data_max = current_value + 800; | ||
| 133 | ring_data_min = current_value - 800; | ||
| 134 | ring_data_default = current_value; | ||
| 135 | } | ||
| 136 | ring_data_max = std::max(ring_data_max, current_value); | ||
| 137 | ring_data_min = std::min(ring_data_min, current_value); | ||
| 138 | calibration = { | ||
| 139 | .default_value = ring_data_default, | ||
| 140 | .max_value = ring_data_max, | ||
| 141 | .min_value = ring_data_min, | ||
| 142 | }; | ||
| 143 | return DriverResult::Success; | ||
| 144 | } | ||
| 145 | |||
| 146 | void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) { | ||
| 147 | constexpr u16 DefaultStickCenter{2048}; | ||
| 148 | constexpr u16 DefaultStickRange{1740}; | ||
| 149 | |||
| 150 | if (calibration.x.center == 0xFFF || calibration.x.center == 0) { | ||
| 151 | calibration.x.center = DefaultStickCenter; | ||
| 152 | } | ||
| 153 | if (calibration.x.max == 0xFFF || calibration.x.max == 0) { | ||
| 154 | calibration.x.max = DefaultStickRange; | ||
| 155 | } | ||
| 156 | if (calibration.x.min == 0xFFF || calibration.x.min == 0) { | ||
| 157 | calibration.x.min = DefaultStickRange; | ||
| 158 | } | ||
| 159 | |||
| 160 | if (calibration.y.center == 0xFFF || calibration.y.center == 0) { | ||
| 161 | calibration.y.center = DefaultStickCenter; | ||
| 162 | } | ||
| 163 | if (calibration.y.max == 0xFFF || calibration.y.max == 0) { | ||
| 164 | calibration.y.max = DefaultStickRange; | ||
| 165 | } | ||
| 166 | if (calibration.y.min == 0xFFF || calibration.y.min == 0) { | ||
| 167 | calibration.y.min = DefaultStickRange; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) { | ||
| 172 | for (auto& sensor : calibration.accelerometer) { | ||
| 173 | if (sensor.scale == 0) { | ||
| 174 | sensor.scale = 0x4000; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | for (auto& sensor : calibration.gyro) { | ||
| 178 | if (sensor.scale == 0) { | ||
| 179 | sensor.scale = 0x3be7; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h new file mode 100644 index 000000000..afb52a36a --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/calibration.h | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "input_common/helpers/joycon_protocol/common_protocol.h" | ||
| 14 | |||
| 15 | namespace InputCommon::Joycon { | ||
| 16 | enum class DriverResult; | ||
| 17 | struct JoyStickCalibration; | ||
| 18 | struct IMUCalibration; | ||
| 19 | struct JoyconHandle; | ||
| 20 | } // namespace InputCommon::Joycon | ||
| 21 | |||
| 22 | namespace InputCommon::Joycon { | ||
| 23 | |||
| 24 | /// Driver functions related to retrieving calibration data from the device | ||
| 25 | class CalibrationProtocol final : private JoyconCommonProtocol { | ||
| 26 | public: | ||
| 27 | explicit CalibrationProtocol(std::shared_ptr<JoyconHandle> handle); | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Sends a request to obtain the left stick calibration from memory | ||
| 31 | * @param is_factory_calibration if true factory values will be returned | ||
| 32 | * @returns JoyStickCalibration of the left joystick | ||
| 33 | */ | ||
| 34 | DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration); | ||
| 35 | |||
| 36 | /** | ||
| 37 | * Sends a request to obtain the right stick calibration from memory | ||
| 38 | * @param is_factory_calibration if true factory values will be returned | ||
| 39 | * @returns JoyStickCalibration of the right joystick | ||
| 40 | */ | ||
| 41 | DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Sends a request to obtain the motion calibration from memory | ||
| 45 | * @returns ImuCalibration of the motion sensor | ||
| 46 | */ | ||
| 47 | DriverResult GetImuCalibration(MotionCalibration& calibration); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Calculates on run time the proper calibration of the ring controller | ||
| 51 | * @returns RingCalibration of the ring sensor | ||
| 52 | */ | ||
| 53 | DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); | ||
| 54 | |||
| 55 | private: | ||
| 56 | void ValidateCalibration(JoyStickCalibration& calibration); | ||
| 57 | void ValidateCalibration(MotionCalibration& calibration); | ||
| 58 | |||
| 59 | s16 ring_data_max = 0; | ||
| 60 | s16 ring_data_default = 0; | ||
| 61 | s16 ring_data_min = 0; | ||
| 62 | }; | ||
| 63 | |||
| 64 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp new file mode 100644 index 000000000..417d0dcc5 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp | |||
| @@ -0,0 +1,299 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "input_common/helpers/joycon_protocol/common_protocol.h" | ||
| 6 | |||
| 7 | namespace InputCommon::Joycon { | ||
| 8 | JoyconCommonProtocol::JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_) | ||
| 9 | : hidapi_handle{std::move(hidapi_handle_)} {} | ||
| 10 | |||
| 11 | u8 JoyconCommonProtocol::GetCounter() { | ||
| 12 | hidapi_handle->packet_counter = (hidapi_handle->packet_counter + 1) & 0x0F; | ||
| 13 | return hidapi_handle->packet_counter; | ||
| 14 | } | ||
| 15 | |||
| 16 | void JoyconCommonProtocol::SetBlocking() { | ||
| 17 | SDL_hid_set_nonblocking(hidapi_handle->handle, 0); | ||
| 18 | } | ||
| 19 | |||
| 20 | void JoyconCommonProtocol::SetNonBlocking() { | ||
| 21 | SDL_hid_set_nonblocking(hidapi_handle->handle, 1); | ||
| 22 | } | ||
| 23 | |||
| 24 | DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { | ||
| 25 | std::vector<u8> buffer; | ||
| 26 | const auto result = ReadSPI(CalAddr::DEVICE_TYPE, 1, buffer); | ||
| 27 | controller_type = ControllerType::None; | ||
| 28 | |||
| 29 | if (result == DriverResult::Success) { | ||
| 30 | controller_type = static_cast<ControllerType>(buffer[0]); | ||
| 31 | // Fallback to 3rd party pro controllers | ||
| 32 | if (controller_type == ControllerType::None) { | ||
| 33 | controller_type = ControllerType::Pro; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | return result; | ||
| 38 | } | ||
| 39 | |||
| 40 | DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) { | ||
| 41 | ControllerType controller_type{ControllerType::None}; | ||
| 42 | const auto result = GetDeviceType(controller_type); | ||
| 43 | if (result != DriverResult::Success || controller_type == ControllerType::None) { | ||
| 44 | return DriverResult::UnsupportedControllerType; | ||
| 45 | } | ||
| 46 | |||
| 47 | hidapi_handle->handle = | ||
| 48 | SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number); | ||
| 49 | |||
| 50 | if (!hidapi_handle->handle) { | ||
| 51 | LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.", | ||
| 52 | device_info->vendor_id, device_info->product_id); | ||
| 53 | return DriverResult::HandleInUse; | ||
| 54 | } | ||
| 55 | |||
| 56 | SetNonBlocking(); | ||
| 57 | return DriverResult::Success; | ||
| 58 | } | ||
| 59 | |||
| 60 | DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) { | ||
| 61 | const std::array<u8, 1> buffer{static_cast<u8>(report_mode)}; | ||
| 62 | return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer); | ||
| 63 | } | ||
| 64 | |||
| 65 | DriverResult JoyconCommonProtocol::SendData(std::span<const u8> buffer) { | ||
| 66 | const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size()); | ||
| 67 | |||
| 68 | if (result == -1) { | ||
| 69 | return DriverResult::ErrorWritingData; | ||
| 70 | } | ||
| 71 | |||
| 72 | return DriverResult::Success; | ||
| 73 | } | ||
| 74 | |||
| 75 | DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vector<u8>& output) { | ||
| 76 | constexpr int timeout_mili = 66; | ||
| 77 | constexpr int MaxTries = 15; | ||
| 78 | int tries = 0; | ||
| 79 | output.resize(MaxSubCommandResponseSize); | ||
| 80 | |||
| 81 | do { | ||
| 82 | int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), | ||
| 83 | MaxSubCommandResponseSize, timeout_mili); | ||
| 84 | |||
| 85 | if (result < 1) { | ||
| 86 | LOG_ERROR(Input, "No response from joycon"); | ||
| 87 | } | ||
| 88 | if (tries++ > MaxTries) { | ||
| 89 | return DriverResult::Timeout; | ||
| 90 | } | ||
| 91 | } while (output[0] != 0x21 && output[14] != static_cast<u8>(sc)); | ||
| 92 | |||
| 93 | if (output[0] != 0x21 && output[14] != static_cast<u8>(sc)) { | ||
| 94 | return DriverResult::WrongReply; | ||
| 95 | } | ||
| 96 | |||
| 97 | return DriverResult::Success; | ||
| 98 | } | ||
| 99 | |||
| 100 | DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer, | ||
| 101 | std::vector<u8>& output) { | ||
| 102 | std::vector<u8> local_buffer(MaxResponseSize); | ||
| 103 | |||
| 104 | local_buffer[0] = static_cast<u8>(OutputReport::RUMBLE_AND_SUBCMD); | ||
| 105 | local_buffer[1] = GetCounter(); | ||
| 106 | local_buffer[10] = static_cast<u8>(sc); | ||
| 107 | for (std::size_t i = 0; i < buffer.size(); ++i) { | ||
| 108 | local_buffer[11 + i] = buffer[i]; | ||
| 109 | } | ||
| 110 | |||
| 111 | auto result = SendData(local_buffer); | ||
| 112 | |||
| 113 | if (result != DriverResult::Success) { | ||
| 114 | return result; | ||
| 115 | } | ||
| 116 | |||
| 117 | result = GetSubCommandResponse(sc, output); | ||
| 118 | |||
| 119 | return DriverResult::Success; | ||
| 120 | } | ||
| 121 | |||
| 122 | DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { | ||
| 123 | std::vector<u8> output; | ||
| 124 | return SendSubCommand(sc, buffer, output); | ||
| 125 | } | ||
| 126 | |||
| 127 | DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) { | ||
| 128 | std::vector<u8> local_buffer(MaxResponseSize); | ||
| 129 | |||
| 130 | local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); | ||
| 131 | local_buffer[1] = GetCounter(); | ||
| 132 | local_buffer[10] = static_cast<u8>(sc); | ||
| 133 | for (std::size_t i = 0; i < buffer.size(); ++i) { | ||
| 134 | local_buffer[11 + i] = buffer[i]; | ||
| 135 | } | ||
| 136 | |||
| 137 | return SendData(local_buffer); | ||
| 138 | } | ||
| 139 | |||
| 140 | DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) { | ||
| 141 | std::vector<u8> local_buffer(MaxResponseSize); | ||
| 142 | |||
| 143 | local_buffer[0] = static_cast<u8>(Joycon::OutputReport::RUMBLE_ONLY); | ||
| 144 | local_buffer[1] = GetCounter(); | ||
| 145 | |||
| 146 | memcpy(local_buffer.data() + 2, buffer.data(), buffer.size()); | ||
| 147 | |||
| 148 | return SendData(local_buffer); | ||
| 149 | } | ||
| 150 | |||
| 151 | DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output) { | ||
| 152 | constexpr std::size_t MaxTries = 10; | ||
| 153 | std::size_t tries = 0; | ||
| 154 | std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, size}; | ||
| 155 | std::vector<u8> local_buffer(size + 20); | ||
| 156 | |||
| 157 | buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF); | ||
| 158 | buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8); | ||
| 159 | do { | ||
| 160 | const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, local_buffer); | ||
| 161 | if (result != DriverResult::Success) { | ||
| 162 | return result; | ||
| 163 | } | ||
| 164 | |||
| 165 | if (tries++ > MaxTries) { | ||
| 166 | return DriverResult::Timeout; | ||
| 167 | } | ||
| 168 | } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]); | ||
| 169 | |||
| 170 | // Remove header from output | ||
| 171 | output = std::vector<u8>(local_buffer.begin() + 20, local_buffer.begin() + 20 + size); | ||
| 172 | return DriverResult::Success; | ||
| 173 | } | ||
| 174 | |||
| 175 | DriverResult JoyconCommonProtocol::EnableMCU(bool enable) { | ||
| 176 | const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)}; | ||
| 177 | const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state); | ||
| 178 | |||
| 179 | if (result != DriverResult::Success) { | ||
| 180 | LOG_ERROR(Input, "SendMCUData failed with error {}", result); | ||
| 181 | } | ||
| 182 | |||
| 183 | return result; | ||
| 184 | } | ||
| 185 | |||
| 186 | DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) { | ||
| 187 | LOG_DEBUG(Input, "ConfigureMCU"); | ||
| 188 | std::array<u8, sizeof(MCUConfig)> config_buffer; | ||
| 189 | memcpy(config_buffer.data(), &config, sizeof(MCUConfig)); | ||
| 190 | config_buffer[37] = CalculateMCU_CRC8(config_buffer.data() + 1, 36); | ||
| 191 | |||
| 192 | const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer); | ||
| 193 | |||
| 194 | if (result != DriverResult::Success) { | ||
| 195 | LOG_ERROR(Input, "Set MCU config failed with error {}", result); | ||
| 196 | } | ||
| 197 | |||
| 198 | return result; | ||
| 199 | } | ||
| 200 | |||
| 201 | DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_, | ||
| 202 | std::vector<u8>& output) { | ||
| 203 | const int report_mode = static_cast<u8>(report_mode_); | ||
| 204 | constexpr int TimeoutMili = 200; | ||
| 205 | constexpr int MaxTries = 9; | ||
| 206 | int tries = 0; | ||
| 207 | output.resize(0x170); | ||
| 208 | |||
| 209 | do { | ||
| 210 | int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), 0x170, TimeoutMili); | ||
| 211 | |||
| 212 | if (result < 1) { | ||
| 213 | LOG_ERROR(Input, "No response from joycon attempt {}", tries); | ||
| 214 | } | ||
| 215 | if (tries++ > MaxTries) { | ||
| 216 | return DriverResult::Timeout; | ||
| 217 | } | ||
| 218 | } while (output[0] != report_mode || output[49] == 0xFF); | ||
| 219 | |||
| 220 | if (output[0] != report_mode || output[49] == 0xFF) { | ||
| 221 | return DriverResult::WrongReply; | ||
| 222 | } | ||
| 223 | |||
| 224 | return DriverResult::Success; | ||
| 225 | } | ||
| 226 | |||
| 227 | DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc, | ||
| 228 | std::span<const u8> buffer, | ||
| 229 | std::vector<u8>& output) { | ||
| 230 | std::vector<u8> local_buffer(MaxResponseSize); | ||
| 231 | |||
| 232 | local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); | ||
| 233 | local_buffer[1] = GetCounter(); | ||
| 234 | local_buffer[9] = static_cast<u8>(sc); | ||
| 235 | for (std::size_t i = 0; i < buffer.size(); ++i) { | ||
| 236 | local_buffer[10 + i] = buffer[i]; | ||
| 237 | } | ||
| 238 | |||
| 239 | auto result = SendData(local_buffer); | ||
| 240 | |||
| 241 | if (result != DriverResult::Success) { | ||
| 242 | return result; | ||
| 243 | } | ||
| 244 | |||
| 245 | result = GetMCUDataResponse(report_mode, output); | ||
| 246 | |||
| 247 | return DriverResult::Success; | ||
| 248 | } | ||
| 249 | |||
| 250 | DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) { | ||
| 251 | std::vector<u8> output; | ||
| 252 | constexpr std::size_t MaxTries{8}; | ||
| 253 | std::size_t tries{}; | ||
| 254 | |||
| 255 | do { | ||
| 256 | const std::vector<u8> mcu_data{static_cast<u8>(MCUMode::Standby)}; | ||
| 257 | const auto result = SendMCUData(report_mode, SubCommand::STATE, mcu_data, output); | ||
| 258 | |||
| 259 | if (result != DriverResult::Success) { | ||
| 260 | return result; | ||
| 261 | } | ||
| 262 | |||
| 263 | if (tries++ > MaxTries) { | ||
| 264 | return DriverResult::WrongReply; | ||
| 265 | } | ||
| 266 | } while (output[49] != 1 || output[56] != static_cast<u8>(mode)); | ||
| 267 | |||
| 268 | return DriverResult::Success; | ||
| 269 | } | ||
| 270 | |||
| 271 | // crc-8-ccitt / polynomial 0x07 look up table | ||
| 272 | constexpr std::array<u8, 256> mcu_crc8_table = { | ||
| 273 | 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, | ||
| 274 | 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, | ||
| 275 | 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, | ||
| 276 | 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, | ||
| 277 | 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, | ||
| 278 | 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, | ||
| 279 | 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, | ||
| 280 | 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, | ||
| 281 | 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, | ||
| 282 | 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, | ||
| 283 | 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, | ||
| 284 | 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, | ||
| 285 | 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, | ||
| 286 | 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, | ||
| 287 | 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, | ||
| 288 | 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3}; | ||
| 289 | |||
| 290 | u8 JoyconCommonProtocol::CalculateMCU_CRC8(u8* buffer, u8 size) const { | ||
| 291 | u8 crc8 = 0x0; | ||
| 292 | |||
| 293 | for (int i = 0; i < size; ++i) { | ||
| 294 | crc8 = mcu_crc8_table[static_cast<u8>(crc8 ^ buffer[i])]; | ||
| 295 | } | ||
| 296 | return crc8; | ||
| 297 | } | ||
| 298 | |||
| 299 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h new file mode 100644 index 000000000..903bcf402 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/common_protocol.h | |||
| @@ -0,0 +1,173 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <memory> | ||
| 12 | #include <span> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 17 | |||
| 18 | namespace InputCommon::Joycon { | ||
| 19 | |||
| 20 | /// Joycon driver functions that handle low level communication | ||
| 21 | class JoyconCommonProtocol { | ||
| 22 | public: | ||
| 23 | explicit JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Sets handle to blocking. In blocking mode, SDL_hid_read() will wait (block) until there is | ||
| 27 | * data to read before returning. | ||
| 28 | */ | ||
| 29 | void SetBlocking(); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Sets handle to non blocking. In non-blocking mode calls to SDL_hid_read() will return | ||
| 33 | * immediately with a value of 0 if there is no data to be read | ||
| 34 | */ | ||
| 35 | void SetNonBlocking(); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Sends a request to obtain the joycon type from device | ||
| 39 | * @returns controller type of the joycon | ||
| 40 | */ | ||
| 41 | DriverResult GetDeviceType(ControllerType& controller_type); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Verifies and sets the joycon_handle if device is valid | ||
| 45 | * @param device info from the driver | ||
| 46 | * @returns success if the device is valid | ||
| 47 | */ | ||
| 48 | DriverResult CheckDeviceAccess(SDL_hid_device_info* device); | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Sends a request to set the polling mode of the joycon | ||
| 52 | * @param report_mode polling mode to be set | ||
| 53 | */ | ||
| 54 | DriverResult SetReportMode(Joycon::ReportMode report_mode); | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Sends data to the joycon device | ||
| 58 | * @param buffer data to be send | ||
| 59 | */ | ||
| 60 | DriverResult SendData(std::span<const u8> buffer); | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Waits for incoming data of the joycon device that matchs the subcommand | ||
| 64 | * @param sub_command type of data to be returned | ||
| 65 | * @returns a buffer containing the responce | ||
| 66 | */ | ||
| 67 | DriverResult GetSubCommandResponse(SubCommand sub_command, std::vector<u8>& output); | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Sends a sub command to the device and waits for it's reply | ||
| 71 | * @param sc sub command to be send | ||
| 72 | * @param buffer data to be send | ||
| 73 | * @returns output buffer containing the responce | ||
| 74 | */ | ||
| 75 | DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, std::vector<u8>& output); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Sends a sub command to the device and waits for it's reply and ignores the output | ||
| 79 | * @param sc sub command to be send | ||
| 80 | * @param buffer data to be send | ||
| 81 | */ | ||
| 82 | DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Sends a mcu command to the device | ||
| 86 | * @param sc sub command to be send | ||
| 87 | * @param buffer data to be send | ||
| 88 | */ | ||
| 89 | DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer); | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Sends vibration data to the joycon | ||
| 93 | * @param buffer data to be send | ||
| 94 | */ | ||
| 95 | DriverResult SendVibrationReport(std::span<const u8> buffer); | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Reads the SPI memory stored on the joycon | ||
| 99 | * @param Initial address location | ||
| 100 | * @param size in bytes to be read | ||
| 101 | * @returns output buffer containing the responce | ||
| 102 | */ | ||
| 103 | DriverResult ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output); | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Enables MCU chip on the joycon | ||
| 107 | * @param enable if true the chip will be enabled | ||
| 108 | */ | ||
| 109 | DriverResult EnableMCU(bool enable); | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Configures the MCU to the correspoinding mode | ||
| 113 | * @param MCUConfig configuration | ||
| 114 | */ | ||
| 115 | DriverResult ConfigureMCU(const MCUConfig& config); | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Waits until there's MCU data available. On timeout returns error | ||
| 119 | * @param report mode of the expected reply | ||
| 120 | * @returns a buffer containing the responce | ||
| 121 | */ | ||
| 122 | DriverResult GetMCUDataResponse(ReportMode report_mode_, std::vector<u8>& output); | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Sends data to the MCU chip and waits for it's reply | ||
| 126 | * @param report mode of the expected reply | ||
| 127 | * @param sub command to be send | ||
| 128 | * @param buffer data to be send | ||
| 129 | * @returns output buffer containing the responce | ||
| 130 | */ | ||
| 131 | DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer, | ||
| 132 | std::vector<u8>& output); | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Wait's until the MCU chip is on the specified mode | ||
| 136 | * @param report mode of the expected reply | ||
| 137 | * @param MCUMode configuration | ||
| 138 | */ | ||
| 139 | DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode); | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Calculates the checksum from the MCU data | ||
| 143 | * @param buffer containing the data to be send | ||
| 144 | * @param size of the buffer in bytes | ||
| 145 | * @returns byte with the correct checksum | ||
| 146 | */ | ||
| 147 | u8 CalculateMCU_CRC8(u8* buffer, u8 size) const; | ||
| 148 | |||
| 149 | private: | ||
| 150 | /** | ||
| 151 | * Increments and returns the packet counter of the handle | ||
| 152 | * @param joycon_handle device to send the data | ||
| 153 | * @returns packet counter value | ||
| 154 | */ | ||
| 155 | u8 GetCounter(); | ||
| 156 | |||
| 157 | std::shared_ptr<JoyconHandle> hidapi_handle; | ||
| 158 | }; | ||
| 159 | |||
| 160 | class ScopedSetBlocking { | ||
| 161 | public: | ||
| 162 | explicit ScopedSetBlocking(JoyconCommonProtocol* self) : m_self{self} { | ||
| 163 | m_self->SetBlocking(); | ||
| 164 | } | ||
| 165 | |||
| 166 | ~ScopedSetBlocking() { | ||
| 167 | m_self->SetNonBlocking(); | ||
| 168 | } | ||
| 169 | |||
| 170 | private: | ||
| 171 | JoyconCommonProtocol* m_self{}; | ||
| 172 | }; | ||
| 173 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp new file mode 100644 index 000000000..63cfb1369 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "input_common/helpers/joycon_protocol/generic_functions.h" | ||
| 6 | |||
| 7 | namespace InputCommon::Joycon { | ||
| 8 | |||
| 9 | GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle) | ||
| 10 | : JoyconCommonProtocol(std::move(handle)) {} | ||
| 11 | |||
| 12 | DriverResult GenericProtocol::EnablePassiveMode() { | ||
| 13 | ScopedSetBlocking sb(this); | ||
| 14 | return SetReportMode(ReportMode::SIMPLE_HID_MODE); | ||
| 15 | } | ||
| 16 | |||
| 17 | DriverResult GenericProtocol::EnableActiveMode() { | ||
| 18 | ScopedSetBlocking sb(this); | ||
| 19 | return SetReportMode(ReportMode::STANDARD_FULL_60HZ); | ||
| 20 | } | ||
| 21 | |||
| 22 | DriverResult GenericProtocol::SetLowPowerMode(bool enable) { | ||
| 23 | ScopedSetBlocking sb(this); | ||
| 24 | const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)}; | ||
| 25 | return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer); | ||
| 26 | } | ||
| 27 | |||
| 28 | DriverResult GenericProtocol::TriggersElapsed() { | ||
| 29 | ScopedSetBlocking sb(this); | ||
| 30 | return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {}); | ||
| 31 | } | ||
| 32 | |||
| 33 | DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { | ||
| 34 | ScopedSetBlocking sb(this); | ||
| 35 | std::vector<u8> output; | ||
| 36 | |||
| 37 | const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); | ||
| 38 | |||
| 39 | device_info = {}; | ||
| 40 | if (result == DriverResult::Success) { | ||
| 41 | memcpy(&device_info, output.data(), sizeof(DeviceInfo)); | ||
| 42 | } | ||
| 43 | |||
| 44 | return result; | ||
| 45 | } | ||
| 46 | |||
| 47 | DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) { | ||
| 48 | return GetDeviceType(controller_type); | ||
| 49 | } | ||
| 50 | |||
| 51 | DriverResult GenericProtocol::EnableImu(bool enable) { | ||
| 52 | ScopedSetBlocking sb(this); | ||
| 53 | const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)}; | ||
| 54 | return SendSubCommand(SubCommand::ENABLE_IMU, buffer); | ||
| 55 | } | ||
| 56 | |||
| 57 | DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, | ||
| 58 | AccelerometerSensitivity asen, | ||
| 59 | AccelerometerPerformance afrec) { | ||
| 60 | ScopedSetBlocking sb(this); | ||
| 61 | const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen), | ||
| 62 | static_cast<u8>(gfrec), static_cast<u8>(afrec)}; | ||
| 63 | return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer); | ||
| 64 | } | ||
| 65 | |||
| 66 | DriverResult GenericProtocol::GetBattery(u32& battery_level) { | ||
| 67 | // This function is meant to request the high resolution battery status | ||
| 68 | battery_level = 0; | ||
| 69 | return DriverResult::NotSupported; | ||
| 70 | } | ||
| 71 | |||
| 72 | DriverResult GenericProtocol::GetColor(Color& color) { | ||
| 73 | ScopedSetBlocking sb(this); | ||
| 74 | std::vector<u8> buffer; | ||
| 75 | const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer); | ||
| 76 | |||
| 77 | color = {}; | ||
| 78 | if (result == DriverResult::Success) { | ||
| 79 | color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]); | ||
| 80 | color.buttons = static_cast<u32>((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]); | ||
| 81 | color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]); | ||
| 82 | color.right_grip = static_cast<u32>((buffer[9] << 16) | (buffer[10] << 8) | buffer[11]); | ||
| 83 | } | ||
| 84 | |||
| 85 | return result; | ||
| 86 | } | ||
| 87 | |||
| 88 | DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { | ||
| 89 | ScopedSetBlocking sb(this); | ||
| 90 | std::vector<u8> buffer; | ||
| 91 | const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer); | ||
| 92 | |||
| 93 | serial_number = {}; | ||
| 94 | if (result == DriverResult::Success) { | ||
| 95 | memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber)); | ||
| 96 | } | ||
| 97 | |||
| 98 | return result; | ||
| 99 | } | ||
| 100 | |||
| 101 | DriverResult GenericProtocol::GetTemperature(u32& temperature) { | ||
| 102 | // Not all devices have temperature sensor | ||
| 103 | temperature = 25; | ||
| 104 | return DriverResult::NotSupported; | ||
| 105 | } | ||
| 106 | |||
| 107 | DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) { | ||
| 108 | DeviceInfo device_info{}; | ||
| 109 | |||
| 110 | const auto result = GetDeviceInfo(device_info); | ||
| 111 | version = device_info.firmware; | ||
| 112 | |||
| 113 | return result; | ||
| 114 | } | ||
| 115 | |||
| 116 | DriverResult GenericProtocol::SetHomeLight() { | ||
| 117 | ScopedSetBlocking sb(this); | ||
| 118 | static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00}; | ||
| 119 | return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer); | ||
| 120 | } | ||
| 121 | |||
| 122 | DriverResult GenericProtocol::SetLedBusy() { | ||
| 123 | return DriverResult::NotSupported; | ||
| 124 | } | ||
| 125 | |||
| 126 | DriverResult GenericProtocol::SetLedPattern(u8 leds) { | ||
| 127 | ScopedSetBlocking sb(this); | ||
| 128 | const std::array<u8, 1> buffer{leds}; | ||
| 129 | return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer); | ||
| 130 | } | ||
| 131 | |||
| 132 | DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) { | ||
| 133 | return SetLedPattern(static_cast<u8>(leds << 4)); | ||
| 134 | } | ||
| 135 | |||
| 136 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h new file mode 100644 index 000000000..424831e81 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/generic_functions.h | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include "input_common/helpers/joycon_protocol/common_protocol.h" | ||
| 12 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 13 | |||
| 14 | namespace InputCommon::Joycon { | ||
| 15 | |||
| 16 | /// Joycon driver functions that easily implemented | ||
| 17 | class GenericProtocol final : private JoyconCommonProtocol { | ||
| 18 | public: | ||
| 19 | explicit GenericProtocol(std::shared_ptr<JoyconHandle> handle); | ||
| 20 | |||
| 21 | /// Enables passive mode. This mode only sends button data on change. Sticks will return digital | ||
| 22 | /// data instead of analog. Motion will be disabled | ||
| 23 | DriverResult EnablePassiveMode(); | ||
| 24 | |||
| 25 | /// Enables active mode. This mode will return the current status every 5-15ms | ||
| 26 | DriverResult EnableActiveMode(); | ||
| 27 | |||
| 28 | /// Enables or disables the low power mode | ||
| 29 | DriverResult SetLowPowerMode(bool enable); | ||
| 30 | |||
| 31 | /// Unknown function used by the switch | ||
| 32 | DriverResult TriggersElapsed(); | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Sends a request to obtain the joycon firmware and mac from handle | ||
| 36 | * @returns controller device info | ||
| 37 | */ | ||
| 38 | DriverResult GetDeviceInfo(DeviceInfo& controller_type); | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Sends a request to obtain the joycon type from handle | ||
| 42 | * @returns controller type of the joycon | ||
| 43 | */ | ||
| 44 | DriverResult GetControllerType(ControllerType& controller_type); | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Enables motion input | ||
| 48 | * @param enable if true motion data will be enabled | ||
| 49 | */ | ||
| 50 | DriverResult EnableImu(bool enable); | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Configures the motion sensor with the specified parameters | ||
| 54 | * @param gsen gyroscope sensor sensitvity in degrees per second | ||
| 55 | * @param gfrec gyroscope sensor frequency in hertz | ||
| 56 | * @param asen accelerometer sensitivity in G force | ||
| 57 | * @param afrec accelerometer frequency in hertz | ||
| 58 | */ | ||
| 59 | DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec, | ||
| 60 | AccelerometerSensitivity asen, AccelerometerPerformance afrec); | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Request battery level from the device | ||
| 64 | * @returns battery level | ||
| 65 | */ | ||
| 66 | DriverResult GetBattery(u32& battery_level); | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Request joycon colors from the device | ||
| 70 | * @returns colors of the body and buttons | ||
| 71 | */ | ||
| 72 | DriverResult GetColor(Color& color); | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Request joycon serial number from the device | ||
| 76 | * @returns 16 byte serial number | ||
| 77 | */ | ||
| 78 | DriverResult GetSerialNumber(SerialNumber& serial_number); | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Request joycon serial number from the device | ||
| 82 | * @returns 16 byte serial number | ||
| 83 | */ | ||
| 84 | DriverResult GetTemperature(u32& temperature); | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Request joycon serial number from the device | ||
| 88 | * @returns 16 byte serial number | ||
| 89 | */ | ||
| 90 | DriverResult GetVersionNumber(FirmwareVersion& version); | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Sets home led behaviour | ||
| 94 | */ | ||
| 95 | DriverResult SetHomeLight(); | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Sets home led into a slow breathing state | ||
| 99 | */ | ||
| 100 | DriverResult SetLedBusy(); | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Sets the 4 player leds on the joycon on a solid state | ||
| 104 | * @params bit flag containing the led state | ||
| 105 | */ | ||
| 106 | DriverResult SetLedPattern(u8 leds); | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Sets the 4 player leds on the joycon on a blinking state | ||
| 110 | * @returns bit flag containing the led state | ||
| 111 | */ | ||
| 112 | DriverResult SetLedBlinkPattern(u8 leds); | ||
| 113 | }; | ||
| 114 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp new file mode 100644 index 000000000..09e17bc5b --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/irs.cpp | |||
| @@ -0,0 +1,298 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <thread> | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "input_common/helpers/joycon_protocol/irs.h" | ||
| 7 | |||
| 8 | namespace InputCommon::Joycon { | ||
| 9 | |||
| 10 | IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle) | ||
| 11 | : JoyconCommonProtocol(std::move(handle)) {} | ||
| 12 | |||
| 13 | DriverResult IrsProtocol::EnableIrs() { | ||
| 14 | LOG_INFO(Input, "Enable IRS"); | ||
| 15 | ScopedSetBlocking sb(this); | ||
| 16 | DriverResult result{DriverResult::Success}; | ||
| 17 | |||
| 18 | if (result == DriverResult::Success) { | ||
| 19 | result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); | ||
| 20 | } | ||
| 21 | if (result == DriverResult::Success) { | ||
| 22 | result = EnableMCU(true); | ||
| 23 | } | ||
| 24 | if (result == DriverResult::Success) { | ||
| 25 | result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); | ||
| 26 | } | ||
| 27 | if (result == DriverResult::Success) { | ||
| 28 | const MCUConfig config{ | ||
| 29 | .command = MCUCommand::ConfigureMCU, | ||
| 30 | .sub_command = MCUSubCommand::SetMCUMode, | ||
| 31 | .mode = MCUMode::IR, | ||
| 32 | .crc = {}, | ||
| 33 | }; | ||
| 34 | |||
| 35 | result = ConfigureMCU(config); | ||
| 36 | } | ||
| 37 | if (result == DriverResult::Success) { | ||
| 38 | result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR); | ||
| 39 | } | ||
| 40 | if (result == DriverResult::Success) { | ||
| 41 | result = ConfigureIrs(); | ||
| 42 | } | ||
| 43 | if (result == DriverResult::Success) { | ||
| 44 | result = WriteRegistersStep1(); | ||
| 45 | } | ||
| 46 | if (result == DriverResult::Success) { | ||
| 47 | result = WriteRegistersStep2(); | ||
| 48 | } | ||
| 49 | |||
| 50 | is_enabled = true; | ||
| 51 | |||
| 52 | return result; | ||
| 53 | } | ||
| 54 | |||
| 55 | DriverResult IrsProtocol::DisableIrs() { | ||
| 56 | LOG_DEBUG(Input, "Disable IRS"); | ||
| 57 | ScopedSetBlocking sb(this); | ||
| 58 | DriverResult result{DriverResult::Success}; | ||
| 59 | |||
| 60 | if (result == DriverResult::Success) { | ||
| 61 | result = EnableMCU(false); | ||
| 62 | } | ||
| 63 | |||
| 64 | is_enabled = false; | ||
| 65 | |||
| 66 | return result; | ||
| 67 | } | ||
| 68 | |||
| 69 | DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) { | ||
| 70 | irs_mode = mode; | ||
| 71 | switch (format) { | ||
| 72 | case IrsResolution::Size320x240: | ||
| 73 | resolution_code = IrsResolutionCode::Size320x240; | ||
| 74 | fragments = IrsFragments::Size320x240; | ||
| 75 | resolution = IrsResolution::Size320x240; | ||
| 76 | break; | ||
| 77 | case IrsResolution::Size160x120: | ||
| 78 | resolution_code = IrsResolutionCode::Size160x120; | ||
| 79 | fragments = IrsFragments::Size160x120; | ||
| 80 | resolution = IrsResolution::Size160x120; | ||
| 81 | break; | ||
| 82 | case IrsResolution::Size80x60: | ||
| 83 | resolution_code = IrsResolutionCode::Size80x60; | ||
| 84 | fragments = IrsFragments::Size80x60; | ||
| 85 | resolution = IrsResolution::Size80x60; | ||
| 86 | break; | ||
| 87 | case IrsResolution::Size20x15: | ||
| 88 | resolution_code = IrsResolutionCode::Size20x15; | ||
| 89 | fragments = IrsFragments::Size20x15; | ||
| 90 | resolution = IrsResolution::Size20x15; | ||
| 91 | break; | ||
| 92 | case IrsResolution::Size40x30: | ||
| 93 | default: | ||
| 94 | resolution_code = IrsResolutionCode::Size40x30; | ||
| 95 | fragments = IrsFragments::Size40x30; | ||
| 96 | resolution = IrsResolution::Size40x30; | ||
| 97 | break; | ||
| 98 | } | ||
| 99 | |||
| 100 | // Restart feature | ||
| 101 | if (is_enabled) { | ||
| 102 | DisableIrs(); | ||
| 103 | return EnableIrs(); | ||
| 104 | } | ||
| 105 | |||
| 106 | return DriverResult::Success; | ||
| 107 | } | ||
| 108 | |||
| 109 | DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) { | ||
| 110 | const u8 next_packet_fragment = | ||
| 111 | static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1)); | ||
| 112 | |||
| 113 | if (buffer[0] == 0x31 && buffer[49] == 0x03) { | ||
| 114 | u8 new_packet_fragment = buffer[52]; | ||
| 115 | if (new_packet_fragment == next_packet_fragment) { | ||
| 116 | packet_fragment = next_packet_fragment; | ||
| 117 | memcpy(buf_image.data() + (300 * packet_fragment), buffer.data() + 59, 300); | ||
| 118 | |||
| 119 | return RequestFrame(packet_fragment); | ||
| 120 | } | ||
| 121 | |||
| 122 | if (new_packet_fragment == packet_fragment) { | ||
| 123 | return RequestFrame(packet_fragment); | ||
| 124 | } | ||
| 125 | |||
| 126 | return ResendFrame(next_packet_fragment); | ||
| 127 | } | ||
| 128 | |||
| 129 | return RequestFrame(packet_fragment); | ||
| 130 | } | ||
| 131 | |||
| 132 | DriverResult IrsProtocol::ConfigureIrs() { | ||
| 133 | LOG_DEBUG(Input, "Configure IRS"); | ||
| 134 | constexpr std::size_t max_tries = 28; | ||
| 135 | std::vector<u8> output; | ||
| 136 | std::size_t tries = 0; | ||
| 137 | |||
| 138 | const IrsConfigure irs_configuration{ | ||
| 139 | .command = MCUCommand::ConfigureIR, | ||
| 140 | .sub_command = MCUSubCommand::SetDeviceMode, | ||
| 141 | .irs_mode = IrsMode::ImageTransfer, | ||
| 142 | .number_of_fragments = fragments, | ||
| 143 | .mcu_major_version = 0x0500, | ||
| 144 | .mcu_minor_version = 0x1800, | ||
| 145 | .crc = {}, | ||
| 146 | }; | ||
| 147 | buf_image.resize((static_cast<u8>(fragments) + 1) * 300); | ||
| 148 | |||
| 149 | std::array<u8, sizeof(IrsConfigure)> request_data{}; | ||
| 150 | memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure)); | ||
| 151 | request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); | ||
| 152 | do { | ||
| 153 | const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); | ||
| 154 | |||
| 155 | if (result != DriverResult::Success) { | ||
| 156 | return result; | ||
| 157 | } | ||
| 158 | if (tries++ >= max_tries) { | ||
| 159 | return DriverResult::WrongReply; | ||
| 160 | } | ||
| 161 | } while (output[15] != 0x0b); | ||
| 162 | |||
| 163 | return DriverResult::Success; | ||
| 164 | } | ||
| 165 | |||
| 166 | DriverResult IrsProtocol::WriteRegistersStep1() { | ||
| 167 | LOG_DEBUG(Input, "WriteRegistersStep1"); | ||
| 168 | DriverResult result{DriverResult::Success}; | ||
| 169 | constexpr std::size_t max_tries = 28; | ||
| 170 | std::vector<u8> output; | ||
| 171 | std::size_t tries = 0; | ||
| 172 | |||
| 173 | const IrsWriteRegisters irs_registers{ | ||
| 174 | .command = MCUCommand::ConfigureIR, | ||
| 175 | .sub_command = MCUSubCommand::WriteDeviceRegisters, | ||
| 176 | .number_of_registers = 0x9, | ||
| 177 | .registers = | ||
| 178 | { | ||
| 179 | IrsRegister{IrRegistersAddress::Resolution, static_cast<u8>(resolution_code)}, | ||
| 180 | {IrRegistersAddress::ExposureLSB, static_cast<u8>(exposure & 0xff)}, | ||
| 181 | {IrRegistersAddress::ExposureMSB, static_cast<u8>(exposure >> 8)}, | ||
| 182 | {IrRegistersAddress::ExposureTime, 0x00}, | ||
| 183 | {IrRegistersAddress::Leds, static_cast<u8>(leds)}, | ||
| 184 | {IrRegistersAddress::DigitalGainLSB, static_cast<u8>((digital_gain & 0x0f) << 4)}, | ||
| 185 | {IrRegistersAddress::DigitalGainMSB, static_cast<u8>((digital_gain & 0xf0) >> 4)}, | ||
| 186 | {IrRegistersAddress::LedFilter, static_cast<u8>(led_filter)}, | ||
| 187 | {IrRegistersAddress::WhitePixelThreshold, 0xc8}, | ||
| 188 | }, | ||
| 189 | .crc = {}, | ||
| 190 | }; | ||
| 191 | |||
| 192 | std::array<u8, sizeof(IrsWriteRegisters)> request_data{}; | ||
| 193 | memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters)); | ||
| 194 | request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); | ||
| 195 | |||
| 196 | std::array<u8, 38> mcu_request{0x02}; | ||
| 197 | mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); | ||
| 198 | mcu_request[37] = 0xFF; | ||
| 199 | |||
| 200 | if (result != DriverResult::Success) { | ||
| 201 | return result; | ||
| 202 | } | ||
| 203 | |||
| 204 | do { | ||
| 205 | result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); | ||
| 206 | |||
| 207 | // First time we need to set the report mode | ||
| 208 | if (result == DriverResult::Success && tries == 0) { | ||
| 209 | result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); | ||
| 210 | } | ||
| 211 | if (result == DriverResult::Success && tries == 0) { | ||
| 212 | GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output); | ||
| 213 | } | ||
| 214 | |||
| 215 | if (result != DriverResult::Success) { | ||
| 216 | return result; | ||
| 217 | } | ||
| 218 | if (tries++ >= max_tries) { | ||
| 219 | return DriverResult::WrongReply; | ||
| 220 | } | ||
| 221 | } while (!(output[15] == 0x13 && output[17] == 0x07) && output[15] != 0x23); | ||
| 222 | |||
| 223 | return DriverResult::Success; | ||
| 224 | } | ||
| 225 | |||
| 226 | DriverResult IrsProtocol::WriteRegistersStep2() { | ||
| 227 | LOG_DEBUG(Input, "WriteRegistersStep2"); | ||
| 228 | constexpr std::size_t max_tries = 28; | ||
| 229 | std::vector<u8> output; | ||
| 230 | std::size_t tries = 0; | ||
| 231 | |||
| 232 | const IrsWriteRegisters irs_registers{ | ||
| 233 | .command = MCUCommand::ConfigureIR, | ||
| 234 | .sub_command = MCUSubCommand::WriteDeviceRegisters, | ||
| 235 | .number_of_registers = 0x8, | ||
| 236 | .registers = | ||
| 237 | { | ||
| 238 | IrsRegister{IrRegistersAddress::LedIntensitiyMSB, | ||
| 239 | static_cast<u8>(led_intensity >> 8)}, | ||
| 240 | {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)}, | ||
| 241 | {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)}, | ||
| 242 | {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)}, | ||
| 243 | {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)}, | ||
| 244 | {IrRegistersAddress::DenoiseColor, static_cast<u8>(denoise & 0xff)}, | ||
| 245 | {IrRegistersAddress::UpdateTime, 0x2d}, | ||
| 246 | {IrRegistersAddress::FinalizeConfig, 0x01}, | ||
| 247 | }, | ||
| 248 | .crc = {}, | ||
| 249 | }; | ||
| 250 | |||
| 251 | std::array<u8, sizeof(IrsWriteRegisters)> request_data{}; | ||
| 252 | memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters)); | ||
| 253 | request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); | ||
| 254 | do { | ||
| 255 | const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); | ||
| 256 | |||
| 257 | if (result != DriverResult::Success) { | ||
| 258 | return result; | ||
| 259 | } | ||
| 260 | if (tries++ >= max_tries) { | ||
| 261 | return DriverResult::WrongReply; | ||
| 262 | } | ||
| 263 | } while (output[15] != 0x13 && output[15] != 0x23); | ||
| 264 | |||
| 265 | return DriverResult::Success; | ||
| 266 | } | ||
| 267 | |||
| 268 | DriverResult IrsProtocol::RequestFrame(u8 frame) { | ||
| 269 | std::array<u8, 38> mcu_request{}; | ||
| 270 | mcu_request[3] = frame; | ||
| 271 | mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); | ||
| 272 | mcu_request[37] = 0xFF; | ||
| 273 | return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); | ||
| 274 | } | ||
| 275 | |||
| 276 | DriverResult IrsProtocol::ResendFrame(u8 frame) { | ||
| 277 | std::array<u8, 38> mcu_request{}; | ||
| 278 | mcu_request[1] = 0x1; | ||
| 279 | mcu_request[2] = frame; | ||
| 280 | mcu_request[3] = 0x0; | ||
| 281 | mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); | ||
| 282 | mcu_request[37] = 0xFF; | ||
| 283 | return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request); | ||
| 284 | } | ||
| 285 | |||
| 286 | std::vector<u8> IrsProtocol::GetImage() const { | ||
| 287 | return buf_image; | ||
| 288 | } | ||
| 289 | |||
| 290 | IrsResolution IrsProtocol::GetIrsFormat() const { | ||
| 291 | return resolution; | ||
| 292 | } | ||
| 293 | |||
| 294 | bool IrsProtocol::IsEnabled() const { | ||
| 295 | return is_enabled; | ||
| 296 | } | ||
| 297 | |||
| 298 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/irs.h b/src/input_common/helpers/joycon_protocol/irs.h new file mode 100644 index 000000000..76dfa02ea --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/irs.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "input_common/helpers/joycon_protocol/common_protocol.h" | ||
| 14 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 15 | |||
| 16 | namespace InputCommon::Joycon { | ||
| 17 | |||
| 18 | class IrsProtocol final : private JoyconCommonProtocol { | ||
| 19 | public: | ||
| 20 | explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle); | ||
| 21 | |||
| 22 | DriverResult EnableIrs(); | ||
| 23 | |||
| 24 | DriverResult DisableIrs(); | ||
| 25 | |||
| 26 | DriverResult SetIrsConfig(IrsMode mode, IrsResolution format); | ||
| 27 | |||
| 28 | DriverResult RequestImage(std::span<u8> buffer); | ||
| 29 | |||
| 30 | std::vector<u8> GetImage() const; | ||
| 31 | |||
| 32 | IrsResolution GetIrsFormat() const; | ||
| 33 | |||
| 34 | bool IsEnabled() const; | ||
| 35 | |||
| 36 | private: | ||
| 37 | DriverResult ConfigureIrs(); | ||
| 38 | |||
| 39 | DriverResult WriteRegistersStep1(); | ||
| 40 | DriverResult WriteRegistersStep2(); | ||
| 41 | |||
| 42 | DriverResult RequestFrame(u8 frame); | ||
| 43 | DriverResult ResendFrame(u8 frame); | ||
| 44 | |||
| 45 | IrsMode irs_mode{IrsMode::ImageTransfer}; | ||
| 46 | IrsResolution resolution{IrsResolution::Size40x30}; | ||
| 47 | IrsResolutionCode resolution_code{IrsResolutionCode::Size40x30}; | ||
| 48 | IrsFragments fragments{IrsFragments::Size40x30}; | ||
| 49 | IrLeds leds{IrLeds::BrightAndDim}; | ||
| 50 | IrExLedFilter led_filter{IrExLedFilter::Enabled}; | ||
| 51 | IrImageFlip image_flip{IrImageFlip::Normal}; | ||
| 52 | u8 digital_gain{0x01}; | ||
| 53 | u16 exposure{0x2490}; | ||
| 54 | u16 led_intensity{0x0f10}; | ||
| 55 | u32 denoise{0x012344}; | ||
| 56 | |||
| 57 | u8 packet_fragment{}; | ||
| 58 | std::vector<u8> buf_image; // 8bpp greyscale image. | ||
| 59 | |||
| 60 | bool is_enabled{}; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h new file mode 100644 index 000000000..182d2c15b --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h | |||
| @@ -0,0 +1,613 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <array> | ||
| 12 | #include <functional> | ||
| 13 | #include <SDL_hidapi.h> | ||
| 14 | |||
| 15 | #include "common/bit_field.h" | ||
| 16 | #include "common/common_funcs.h" | ||
| 17 | #include "common/common_types.h" | ||
| 18 | |||
| 19 | namespace InputCommon::Joycon { | ||
| 20 | constexpr u32 MaxErrorCount = 50; | ||
| 21 | constexpr u32 MaxBufferSize = 368; | ||
| 22 | constexpr u32 MaxResponseSize = 49; | ||
| 23 | constexpr u32 MaxSubCommandResponseSize = 64; | ||
| 24 | constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40}; | ||
| 25 | |||
| 26 | using MacAddress = std::array<u8, 6>; | ||
| 27 | using SerialNumber = std::array<u8, 15>; | ||
| 28 | |||
| 29 | enum class ControllerType { | ||
| 30 | None, | ||
| 31 | Left, | ||
| 32 | Right, | ||
| 33 | Pro, | ||
| 34 | Grip, | ||
| 35 | Dual, | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum class PadAxes { | ||
| 39 | LeftStickX, | ||
| 40 | LeftStickY, | ||
| 41 | RightStickX, | ||
| 42 | RightStickY, | ||
| 43 | Undefined, | ||
| 44 | }; | ||
| 45 | |||
| 46 | enum class PadMotion { | ||
| 47 | LeftMotion, | ||
| 48 | RightMotion, | ||
| 49 | Undefined, | ||
| 50 | }; | ||
| 51 | |||
| 52 | enum class PadButton : u32 { | ||
| 53 | Down = 0x000001, | ||
| 54 | Up = 0x000002, | ||
| 55 | Right = 0x000004, | ||
| 56 | Left = 0x000008, | ||
| 57 | LeftSR = 0x000010, | ||
| 58 | LeftSL = 0x000020, | ||
| 59 | L = 0x000040, | ||
| 60 | ZL = 0x000080, | ||
| 61 | Y = 0x000100, | ||
| 62 | X = 0x000200, | ||
| 63 | B = 0x000400, | ||
| 64 | A = 0x000800, | ||
| 65 | RightSR = 0x001000, | ||
| 66 | RightSL = 0x002000, | ||
| 67 | R = 0x004000, | ||
| 68 | ZR = 0x008000, | ||
| 69 | Minus = 0x010000, | ||
| 70 | Plus = 0x020000, | ||
| 71 | StickR = 0x040000, | ||
| 72 | StickL = 0x080000, | ||
| 73 | Home = 0x100000, | ||
| 74 | Capture = 0x200000, | ||
| 75 | }; | ||
| 76 | |||
| 77 | enum class PasivePadButton : u32 { | ||
| 78 | Down_A = 0x0001, | ||
| 79 | Right_X = 0x0002, | ||
| 80 | Left_B = 0x0004, | ||
| 81 | Up_Y = 0x0008, | ||
| 82 | SL = 0x0010, | ||
| 83 | SR = 0x0020, | ||
| 84 | Minus = 0x0100, | ||
| 85 | Plus = 0x0200, | ||
| 86 | StickL = 0x0400, | ||
| 87 | StickR = 0x0800, | ||
| 88 | Home = 0x1000, | ||
| 89 | Capture = 0x2000, | ||
| 90 | L_R = 0x4000, | ||
| 91 | ZL_ZR = 0x8000, | ||
| 92 | }; | ||
| 93 | |||
| 94 | enum class OutputReport : u8 { | ||
| 95 | RUMBLE_AND_SUBCMD = 0x01, | ||
| 96 | FW_UPDATE_PKT = 0x03, | ||
| 97 | RUMBLE_ONLY = 0x10, | ||
| 98 | MCU_DATA = 0x11, | ||
| 99 | USB_CMD = 0x80, | ||
| 100 | }; | ||
| 101 | |||
| 102 | enum class InputReport : u8 { | ||
| 103 | SUBCMD_REPLY = 0x21, | ||
| 104 | STANDARD_FULL_60HZ = 0x30, | ||
| 105 | NFC_IR_MODE_60HZ = 0x31, | ||
| 106 | SIMPLE_HID_MODE = 0x3F, | ||
| 107 | INPUT_USB_RESPONSE = 0x81, | ||
| 108 | }; | ||
| 109 | |||
| 110 | enum class FeatureReport : u8 { | ||
| 111 | Last_SUBCMD = 0x02, | ||
| 112 | OTA_GW_UPGRADE = 0x70, | ||
| 113 | SETUP_MEM_READ = 0x71, | ||
| 114 | MEM_READ = 0x72, | ||
| 115 | ERASE_MEM_SECTOR = 0x73, | ||
| 116 | MEM_WRITE = 0x74, | ||
| 117 | LAUNCH = 0x75, | ||
| 118 | }; | ||
| 119 | |||
| 120 | enum class SubCommand : u8 { | ||
| 121 | STATE = 0x00, | ||
| 122 | MANUAL_BT_PAIRING = 0x01, | ||
| 123 | REQ_DEV_INFO = 0x02, | ||
| 124 | SET_REPORT_MODE = 0x03, | ||
| 125 | TRIGGERS_ELAPSED = 0x04, | ||
| 126 | GET_PAGE_LIST_STATE = 0x05, | ||
| 127 | SET_HCI_STATE = 0x06, | ||
| 128 | RESET_PAIRING_INFO = 0x07, | ||
| 129 | LOW_POWER_MODE = 0x08, | ||
| 130 | SPI_FLASH_READ = 0x10, | ||
| 131 | SPI_FLASH_WRITE = 0x11, | ||
| 132 | SPI_SECTOR_ERASE = 0x12, | ||
| 133 | RESET_MCU = 0x20, | ||
| 134 | SET_MCU_CONFIG = 0x21, | ||
| 135 | SET_MCU_STATE = 0x22, | ||
| 136 | SET_PLAYER_LIGHTS = 0x30, | ||
| 137 | GET_PLAYER_LIGHTS = 0x31, | ||
| 138 | SET_HOME_LIGHT = 0x38, | ||
| 139 | ENABLE_IMU = 0x40, | ||
| 140 | SET_IMU_SENSITIVITY = 0x41, | ||
| 141 | WRITE_IMU_REG = 0x42, | ||
| 142 | READ_IMU_REG = 0x43, | ||
| 143 | ENABLE_VIBRATION = 0x48, | ||
| 144 | GET_REGULATED_VOLTAGE = 0x50, | ||
| 145 | SET_EXTERNAL_CONFIG = 0x58, | ||
| 146 | UNKNOWN_RINGCON = 0x59, | ||
| 147 | UNKNOWN_RINGCON2 = 0x5A, | ||
| 148 | UNKNOWN_RINGCON3 = 0x5C, | ||
| 149 | }; | ||
| 150 | |||
| 151 | enum class UsbSubCommand : u8 { | ||
| 152 | CONN_STATUS = 0x01, | ||
| 153 | HADSHAKE = 0x02, | ||
| 154 | BAUDRATE_3M = 0x03, | ||
| 155 | NO_TIMEOUT = 0x04, | ||
| 156 | EN_TIMEOUT = 0x05, | ||
| 157 | RESET = 0x06, | ||
| 158 | PRE_HANDSHAKE = 0x91, | ||
| 159 | SEND_UART = 0x92, | ||
| 160 | }; | ||
| 161 | |||
| 162 | enum class CalMagic : u8 { | ||
| 163 | USR_MAGIC_0 = 0xB2, | ||
| 164 | USR_MAGIC_1 = 0xA1, | ||
| 165 | USRR_MAGI_SIZE = 2, | ||
| 166 | }; | ||
| 167 | |||
| 168 | enum class CalAddr { | ||
| 169 | SERIAL_NUMBER = 0X6000, | ||
| 170 | DEVICE_TYPE = 0X6012, | ||
| 171 | COLOR_EXIST = 0X601B, | ||
| 172 | FACT_LEFT_DATA = 0X603d, | ||
| 173 | FACT_RIGHT_DATA = 0X6046, | ||
| 174 | COLOR_DATA = 0X6050, | ||
| 175 | FACT_IMU_DATA = 0X6020, | ||
| 176 | USER_LEFT_MAGIC = 0X8010, | ||
| 177 | USER_LEFT_DATA = 0X8012, | ||
| 178 | USER_RIGHT_MAGIC = 0X801B, | ||
| 179 | USER_RIGHT_DATA = 0X801D, | ||
| 180 | USER_IMU_MAGIC = 0X8026, | ||
| 181 | USER_IMU_DATA = 0X8028, | ||
| 182 | }; | ||
| 183 | |||
| 184 | enum class ReportMode : u8 { | ||
| 185 | ACTIVE_POLLING_NFC_IR_CAMERA_DATA = 0x00, | ||
| 186 | ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01, | ||
| 187 | ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02, | ||
| 188 | ACTIVE_POLLING_IR_CAMERA_DATA = 0x03, | ||
| 189 | MCU_UPDATE_STATE = 0x23, | ||
| 190 | STANDARD_FULL_60HZ = 0x30, | ||
| 191 | NFC_IR_MODE_60HZ = 0x31, | ||
| 192 | SIMPLE_HID_MODE = 0x3F, | ||
| 193 | }; | ||
| 194 | |||
| 195 | enum class GyroSensitivity : u8 { | ||
| 196 | DPS250, | ||
| 197 | DPS500, | ||
| 198 | DPS1000, | ||
| 199 | DPS2000, // Default | ||
| 200 | }; | ||
| 201 | |||
| 202 | enum class AccelerometerSensitivity : u8 { | ||
| 203 | G8, // Default | ||
| 204 | G4, | ||
| 205 | G2, | ||
| 206 | G16, | ||
| 207 | }; | ||
| 208 | |||
| 209 | enum class GyroPerformance : u8 { | ||
| 210 | HZ833, | ||
| 211 | HZ208, // Default | ||
| 212 | }; | ||
| 213 | |||
| 214 | enum class AccelerometerPerformance : u8 { | ||
| 215 | HZ200, | ||
| 216 | HZ100, // Default | ||
| 217 | }; | ||
| 218 | |||
| 219 | enum class MCUCommand : u8 { | ||
| 220 | ConfigureMCU = 0x21, | ||
| 221 | ConfigureIR = 0x23, | ||
| 222 | }; | ||
| 223 | |||
| 224 | enum class MCUSubCommand : u8 { | ||
| 225 | SetMCUMode = 0x0, | ||
| 226 | SetDeviceMode = 0x1, | ||
| 227 | ReadDeviceMode = 0x02, | ||
| 228 | WriteDeviceRegisters = 0x4, | ||
| 229 | }; | ||
| 230 | |||
| 231 | enum class MCUMode : u8 { | ||
| 232 | Suspend = 0, | ||
| 233 | Standby = 1, | ||
| 234 | Ringcon = 3, | ||
| 235 | NFC = 4, | ||
| 236 | IR = 5, | ||
| 237 | MaybeFWUpdate = 6, | ||
| 238 | }; | ||
| 239 | |||
| 240 | enum class MCURequest : u8 { | ||
| 241 | GetMCUStatus = 1, | ||
| 242 | GetNFCData = 2, | ||
| 243 | GetIRData = 3, | ||
| 244 | }; | ||
| 245 | |||
| 246 | enum class MCUReport : u8 { | ||
| 247 | Empty = 0x00, | ||
| 248 | StateReport = 0x01, | ||
| 249 | IRData = 0x03, | ||
| 250 | BusyInitializing = 0x0b, | ||
| 251 | IRStatus = 0x13, | ||
| 252 | IRRegisters = 0x1b, | ||
| 253 | NFCState = 0x2a, | ||
| 254 | NFCReadData = 0x3a, | ||
| 255 | EmptyAwaitingCmd = 0xff, | ||
| 256 | }; | ||
| 257 | |||
| 258 | enum class MCUPacketFlag : u8 { | ||
| 259 | MorePacketsRemaining = 0x00, | ||
| 260 | LastCommandPacket = 0x08, | ||
| 261 | }; | ||
| 262 | |||
| 263 | enum class NFCReadCommand : u8 { | ||
| 264 | CancelAll = 0x00, | ||
| 265 | StartPolling = 0x01, | ||
| 266 | StopPolling = 0x02, | ||
| 267 | StartWaitingRecieve = 0x04, | ||
| 268 | Ntag = 0x06, | ||
| 269 | Mifare = 0x0F, | ||
| 270 | }; | ||
| 271 | |||
| 272 | enum class NFCTagType : u8 { | ||
| 273 | AllTags = 0x00, | ||
| 274 | Ntag215 = 0x01, | ||
| 275 | }; | ||
| 276 | |||
| 277 | enum class NFCPages { | ||
| 278 | Block0 = 0, | ||
| 279 | Block45 = 45, | ||
| 280 | Block135 = 135, | ||
| 281 | Block231 = 231, | ||
| 282 | }; | ||
| 283 | |||
| 284 | enum class NFCStatus : u8 { | ||
| 285 | LastPackage = 0x04, | ||
| 286 | TagLost = 0x07, | ||
| 287 | }; | ||
| 288 | |||
| 289 | enum class IrsMode : u8 { | ||
| 290 | None = 0x02, | ||
| 291 | Moment = 0x03, | ||
| 292 | Dpd = 0x04, | ||
| 293 | Clustering = 0x06, | ||
| 294 | ImageTransfer = 0x07, | ||
| 295 | Silhouette = 0x08, | ||
| 296 | TeraImage = 0x09, | ||
| 297 | SilhouetteTeraImage = 0x0A, | ||
| 298 | }; | ||
| 299 | |||
| 300 | enum class IrsResolution { | ||
| 301 | Size320x240, | ||
| 302 | Size160x120, | ||
| 303 | Size80x60, | ||
| 304 | Size40x30, | ||
| 305 | Size20x15, | ||
| 306 | None, | ||
| 307 | }; | ||
| 308 | |||
| 309 | enum class IrsResolutionCode : u8 { | ||
| 310 | Size320x240 = 0x00, // Full pixel array | ||
| 311 | Size160x120 = 0x50, // Sensor Binning [2 X 2] | ||
| 312 | Size80x60 = 0x64, // Sensor Binning [4 x 2] and Skipping [1 x 2] | ||
| 313 | Size40x30 = 0x69, // Sensor Binning [4 x 2] and Skipping [2 x 4] | ||
| 314 | Size20x15 = 0x6A, // Sensor Binning [4 x 2] and Skipping [4 x 4] | ||
| 315 | }; | ||
| 316 | |||
| 317 | // Size of image divided by 300 | ||
| 318 | enum class IrsFragments : u8 { | ||
| 319 | Size20x15 = 0x00, | ||
| 320 | Size40x30 = 0x03, | ||
| 321 | Size80x60 = 0x0f, | ||
| 322 | Size160x120 = 0x3f, | ||
| 323 | Size320x240 = 0xFF, | ||
| 324 | }; | ||
| 325 | |||
| 326 | enum class IrLeds : u8 { | ||
| 327 | BrightAndDim = 0x00, | ||
| 328 | Bright = 0x20, | ||
| 329 | Dim = 0x10, | ||
| 330 | None = 0x30, | ||
| 331 | }; | ||
| 332 | |||
| 333 | enum class IrExLedFilter : u8 { | ||
| 334 | Disabled = 0x00, | ||
| 335 | Enabled = 0x03, | ||
| 336 | }; | ||
| 337 | |||
| 338 | enum class IrImageFlip : u8 { | ||
| 339 | Normal = 0x00, | ||
| 340 | Inverted = 0x02, | ||
| 341 | }; | ||
| 342 | |||
| 343 | enum class IrRegistersAddress : u16 { | ||
| 344 | UpdateTime = 0x0400, | ||
| 345 | FinalizeConfig = 0x0700, | ||
| 346 | LedFilter = 0x0e00, | ||
| 347 | Leds = 0x1000, | ||
| 348 | LedIntensitiyMSB = 0x1100, | ||
| 349 | LedIntensitiyLSB = 0x1200, | ||
| 350 | ImageFlip = 0x2d00, | ||
| 351 | Resolution = 0x2e00, | ||
| 352 | DigitalGainLSB = 0x2e01, | ||
| 353 | DigitalGainMSB = 0x2f01, | ||
| 354 | ExposureLSB = 0x3001, | ||
| 355 | ExposureMSB = 0x3101, | ||
| 356 | ExposureTime = 0x3201, | ||
| 357 | WhitePixelThreshold = 0x4301, | ||
| 358 | DenoiseSmoothing = 0x6701, | ||
| 359 | DenoiseEdge = 0x6801, | ||
| 360 | DenoiseColor = 0x6901, | ||
| 361 | }; | ||
| 362 | |||
| 363 | enum class DriverResult { | ||
| 364 | Success, | ||
| 365 | WrongReply, | ||
| 366 | Timeout, | ||
| 367 | UnsupportedControllerType, | ||
| 368 | HandleInUse, | ||
| 369 | ErrorReadingData, | ||
| 370 | ErrorWritingData, | ||
| 371 | NoDeviceDetected, | ||
| 372 | InvalidHandle, | ||
| 373 | NotSupported, | ||
| 374 | Disabled, | ||
| 375 | Unknown, | ||
| 376 | }; | ||
| 377 | |||
| 378 | struct MotionSensorCalibration { | ||
| 379 | s16 offset; | ||
| 380 | s16 scale; | ||
| 381 | }; | ||
| 382 | |||
| 383 | struct MotionCalibration { | ||
| 384 | std::array<MotionSensorCalibration, 3> accelerometer; | ||
| 385 | std::array<MotionSensorCalibration, 3> gyro; | ||
| 386 | }; | ||
| 387 | |||
| 388 | // Basic motion data containing data from the sensors and a timestamp in microseconds | ||
| 389 | struct MotionData { | ||
| 390 | float gyro_x{}; | ||
| 391 | float gyro_y{}; | ||
| 392 | float gyro_z{}; | ||
| 393 | float accel_x{}; | ||
| 394 | float accel_y{}; | ||
| 395 | float accel_z{}; | ||
| 396 | u64 delta_timestamp{}; | ||
| 397 | }; | ||
| 398 | |||
| 399 | struct JoyStickAxisCalibration { | ||
| 400 | u16 max{1}; | ||
| 401 | u16 min{1}; | ||
| 402 | u16 center{0}; | ||
| 403 | }; | ||
| 404 | |||
| 405 | struct JoyStickCalibration { | ||
| 406 | JoyStickAxisCalibration x; | ||
| 407 | JoyStickAxisCalibration y; | ||
| 408 | }; | ||
| 409 | |||
| 410 | struct RingCalibration { | ||
| 411 | s16 default_value; | ||
| 412 | s16 max_value; | ||
| 413 | s16 min_value; | ||
| 414 | }; | ||
| 415 | |||
| 416 | struct Color { | ||
| 417 | u32 body; | ||
| 418 | u32 buttons; | ||
| 419 | u32 left_grip; | ||
| 420 | u32 right_grip; | ||
| 421 | }; | ||
| 422 | |||
| 423 | struct Battery { | ||
| 424 | union { | ||
| 425 | u8 raw{}; | ||
| 426 | |||
| 427 | BitField<0, 4, u8> unknown; | ||
| 428 | BitField<4, 1, u8> charging; | ||
| 429 | BitField<5, 3, u8> status; | ||
| 430 | }; | ||
| 431 | }; | ||
| 432 | |||
| 433 | struct VibrationValue { | ||
| 434 | f32 low_amplitude; | ||
| 435 | f32 low_frequency; | ||
| 436 | f32 high_amplitude; | ||
| 437 | f32 high_frequency; | ||
| 438 | }; | ||
| 439 | |||
| 440 | struct JoyconHandle { | ||
| 441 | SDL_hid_device* handle = nullptr; | ||
| 442 | u8 packet_counter{}; | ||
| 443 | }; | ||
| 444 | |||
| 445 | struct MCUConfig { | ||
| 446 | MCUCommand command; | ||
| 447 | MCUSubCommand sub_command; | ||
| 448 | MCUMode mode; | ||
| 449 | INSERT_PADDING_BYTES(0x22); | ||
| 450 | u8 crc; | ||
| 451 | }; | ||
| 452 | static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size"); | ||
| 453 | |||
| 454 | #pragma pack(push, 1) | ||
| 455 | struct InputReportPassive { | ||
| 456 | InputReport report_mode; | ||
| 457 | u16 button_input; | ||
| 458 | u8 stick_state; | ||
| 459 | std::array<u8, 10> unknown_data; | ||
| 460 | }; | ||
| 461 | static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size"); | ||
| 462 | |||
| 463 | struct InputReportActive { | ||
| 464 | InputReport report_mode; | ||
| 465 | u8 packet_id; | ||
| 466 | Battery battery_status; | ||
| 467 | std::array<u8, 3> button_input; | ||
| 468 | std::array<u8, 3> left_stick_state; | ||
| 469 | std::array<u8, 3> right_stick_state; | ||
| 470 | u8 vibration_code; | ||
| 471 | std::array<s16, 6 * 2> motion_input; | ||
| 472 | INSERT_PADDING_BYTES(0x2); | ||
| 473 | s16 ring_input; | ||
| 474 | }; | ||
| 475 | static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size"); | ||
| 476 | |||
| 477 | struct InputReportNfcIr { | ||
| 478 | InputReport report_mode; | ||
| 479 | u8 packet_id; | ||
| 480 | Battery battery_status; | ||
| 481 | std::array<u8, 3> button_input; | ||
| 482 | std::array<u8, 3> left_stick_state; | ||
| 483 | std::array<u8, 3> right_stick_state; | ||
| 484 | u8 vibration_code; | ||
| 485 | std::array<s16, 6 * 2> motion_input; | ||
| 486 | INSERT_PADDING_BYTES(0x4); | ||
| 487 | }; | ||
| 488 | static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size"); | ||
| 489 | #pragma pack(pop) | ||
| 490 | |||
| 491 | struct IMUCalibration { | ||
| 492 | std::array<s16, 3> accelerometer_offset; | ||
| 493 | std::array<s16, 3> accelerometer_scale; | ||
| 494 | std::array<s16, 3> gyroscope_offset; | ||
| 495 | std::array<s16, 3> gyroscope_scale; | ||
| 496 | }; | ||
| 497 | static_assert(sizeof(IMUCalibration) == 0x18, "IMUCalibration is an invalid size"); | ||
| 498 | |||
| 499 | struct NFCReadBlock { | ||
| 500 | u8 start; | ||
| 501 | u8 end; | ||
| 502 | }; | ||
| 503 | static_assert(sizeof(NFCReadBlock) == 0x2, "NFCReadBlock is an invalid size"); | ||
| 504 | |||
| 505 | struct NFCReadBlockCommand { | ||
| 506 | u8 block_count{}; | ||
| 507 | std::array<NFCReadBlock, 4> blocks{}; | ||
| 508 | }; | ||
| 509 | static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an invalid size"); | ||
| 510 | |||
| 511 | struct NFCReadCommandData { | ||
| 512 | u8 unknown; | ||
| 513 | u8 uuid_length; | ||
| 514 | u8 unknown_2; | ||
| 515 | std::array<u8, 6> uid; | ||
| 516 | NFCTagType tag_type; | ||
| 517 | NFCReadBlockCommand read_block; | ||
| 518 | }; | ||
| 519 | static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size"); | ||
| 520 | |||
| 521 | struct NFCPollingCommandData { | ||
| 522 | u8 enable_mifare; | ||
| 523 | u8 unknown_1; | ||
| 524 | u8 unknown_2; | ||
| 525 | u8 unknown_3; | ||
| 526 | u8 unknown_4; | ||
| 527 | }; | ||
| 528 | static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size"); | ||
| 529 | |||
| 530 | struct NFCRequestState { | ||
| 531 | MCUSubCommand sub_command; | ||
| 532 | NFCReadCommand command_argument; | ||
| 533 | u8 packet_id; | ||
| 534 | INSERT_PADDING_BYTES(0x1); | ||
| 535 | MCUPacketFlag packet_flag; | ||
| 536 | u8 data_length; | ||
| 537 | union { | ||
| 538 | std::array<u8, 0x1F> raw_data; | ||
| 539 | NFCReadCommandData nfc_read; | ||
| 540 | NFCPollingCommandData nfc_polling; | ||
| 541 | }; | ||
| 542 | u8 crc; | ||
| 543 | }; | ||
| 544 | static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size"); | ||
| 545 | |||
| 546 | struct IrsConfigure { | ||
| 547 | MCUCommand command; | ||
| 548 | MCUSubCommand sub_command; | ||
| 549 | IrsMode irs_mode; | ||
| 550 | IrsFragments number_of_fragments; | ||
| 551 | u16 mcu_major_version; | ||
| 552 | u16 mcu_minor_version; | ||
| 553 | INSERT_PADDING_BYTES(0x1D); | ||
| 554 | u8 crc; | ||
| 555 | }; | ||
| 556 | static_assert(sizeof(IrsConfigure) == 0x26, "IrsConfigure is an invalid size"); | ||
| 557 | |||
| 558 | #pragma pack(push, 1) | ||
| 559 | struct IrsRegister { | ||
| 560 | IrRegistersAddress address; | ||
| 561 | u8 value; | ||
| 562 | }; | ||
| 563 | static_assert(sizeof(IrsRegister) == 0x3, "IrsRegister is an invalid size"); | ||
| 564 | |||
| 565 | struct IrsWriteRegisters { | ||
| 566 | MCUCommand command; | ||
| 567 | MCUSubCommand sub_command; | ||
| 568 | u8 number_of_registers; | ||
| 569 | std::array<IrsRegister, 9> registers; | ||
| 570 | INSERT_PADDING_BYTES(0x7); | ||
| 571 | u8 crc; | ||
| 572 | }; | ||
| 573 | static_assert(sizeof(IrsWriteRegisters) == 0x26, "IrsWriteRegisters is an invalid size"); | ||
| 574 | #pragma pack(pop) | ||
| 575 | |||
| 576 | struct FirmwareVersion { | ||
| 577 | u8 major; | ||
| 578 | u8 minor; | ||
| 579 | }; | ||
| 580 | static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size"); | ||
| 581 | |||
| 582 | struct DeviceInfo { | ||
| 583 | FirmwareVersion firmware; | ||
| 584 | MacAddress mac_address; | ||
| 585 | }; | ||
| 586 | static_assert(sizeof(DeviceInfo) == 0x8, "DeviceInfo is an invalid size"); | ||
| 587 | |||
| 588 | struct MotionStatus { | ||
| 589 | bool is_enabled; | ||
| 590 | u64 delta_time; | ||
| 591 | GyroSensitivity gyro_sensitivity; | ||
| 592 | AccelerometerSensitivity accelerometer_sensitivity; | ||
| 593 | }; | ||
| 594 | |||
| 595 | struct RingStatus { | ||
| 596 | bool is_enabled; | ||
| 597 | s16 default_value; | ||
| 598 | s16 max_value; | ||
| 599 | s16 min_value; | ||
| 600 | }; | ||
| 601 | |||
| 602 | struct JoyconCallbacks { | ||
| 603 | std::function<void(Battery)> on_battery_data; | ||
| 604 | std::function<void(Color)> on_color_data; | ||
| 605 | std::function<void(int, bool)> on_button_data; | ||
| 606 | std::function<void(int, f32)> on_stick_data; | ||
| 607 | std::function<void(int, const MotionData&)> on_motion_data; | ||
| 608 | std::function<void(f32)> on_ring_data; | ||
| 609 | std::function<void(const std::vector<u8>&)> on_amiibo_data; | ||
| 610 | std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data; | ||
| 611 | }; | ||
| 612 | |||
| 613 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp new file mode 100644 index 000000000..5c0f71722 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp | |||
| @@ -0,0 +1,400 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <thread> | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "input_common/helpers/joycon_protocol/nfc.h" | ||
| 7 | |||
| 8 | namespace InputCommon::Joycon { | ||
| 9 | |||
| 10 | NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle) | ||
| 11 | : JoyconCommonProtocol(std::move(handle)) {} | ||
| 12 | |||
| 13 | DriverResult NfcProtocol::EnableNfc() { | ||
| 14 | LOG_INFO(Input, "Enable NFC"); | ||
| 15 | ScopedSetBlocking sb(this); | ||
| 16 | DriverResult result{DriverResult::Success}; | ||
| 17 | |||
| 18 | if (result == DriverResult::Success) { | ||
| 19 | result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); | ||
| 20 | } | ||
| 21 | if (result == DriverResult::Success) { | ||
| 22 | result = EnableMCU(true); | ||
| 23 | } | ||
| 24 | if (result == DriverResult::Success) { | ||
| 25 | result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); | ||
| 26 | } | ||
| 27 | if (result == DriverResult::Success) { | ||
| 28 | const MCUConfig config{ | ||
| 29 | .command = MCUCommand::ConfigureMCU, | ||
| 30 | .sub_command = MCUSubCommand::SetMCUMode, | ||
| 31 | .mode = MCUMode::NFC, | ||
| 32 | .crc = {}, | ||
| 33 | }; | ||
| 34 | |||
| 35 | result = ConfigureMCU(config); | ||
| 36 | } | ||
| 37 | |||
| 38 | return result; | ||
| 39 | } | ||
| 40 | |||
| 41 | DriverResult NfcProtocol::DisableNfc() { | ||
| 42 | LOG_DEBUG(Input, "Disable NFC"); | ||
| 43 | ScopedSetBlocking sb(this); | ||
| 44 | DriverResult result{DriverResult::Success}; | ||
| 45 | |||
| 46 | if (result == DriverResult::Success) { | ||
| 47 | result = EnableMCU(false); | ||
| 48 | } | ||
| 49 | |||
| 50 | is_enabled = false; | ||
| 51 | |||
| 52 | return result; | ||
| 53 | } | ||
| 54 | |||
| 55 | DriverResult NfcProtocol::StartNFCPollingMode() { | ||
| 56 | LOG_DEBUG(Input, "Start NFC pooling Mode"); | ||
| 57 | ScopedSetBlocking sb(this); | ||
| 58 | DriverResult result{DriverResult::Success}; | ||
| 59 | TagFoundData tag_data{}; | ||
| 60 | |||
| 61 | if (result == DriverResult::Success) { | ||
| 62 | result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); | ||
| 63 | } | ||
| 64 | if (result == DriverResult::Success) { | ||
| 65 | result = WaitUntilNfcIsReady(); | ||
| 66 | } | ||
| 67 | if (result == DriverResult::Success) { | ||
| 68 | is_enabled = true; | ||
| 69 | } | ||
| 70 | |||
| 71 | return result; | ||
| 72 | } | ||
| 73 | |||
| 74 | DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { | ||
| 75 | LOG_DEBUG(Input, "Start NFC pooling Mode"); | ||
| 76 | ScopedSetBlocking sb(this); | ||
| 77 | DriverResult result{DriverResult::Success}; | ||
| 78 | TagFoundData tag_data{}; | ||
| 79 | |||
| 80 | if (result == DriverResult::Success) { | ||
| 81 | result = StartPolling(tag_data); | ||
| 82 | } | ||
| 83 | if (result == DriverResult::Success) { | ||
| 84 | result = ReadTag(tag_data); | ||
| 85 | } | ||
| 86 | if (result == DriverResult::Success) { | ||
| 87 | result = WaitUntilNfcIsReady(); | ||
| 88 | } | ||
| 89 | if (result == DriverResult::Success) { | ||
| 90 | result = StartPolling(tag_data); | ||
| 91 | } | ||
| 92 | if (result == DriverResult::Success) { | ||
| 93 | result = GetAmiiboData(data); | ||
| 94 | } | ||
| 95 | |||
| 96 | return result; | ||
| 97 | } | ||
| 98 | |||
| 99 | bool NfcProtocol::HasAmiibo() { | ||
| 100 | ScopedSetBlocking sb(this); | ||
| 101 | DriverResult result{DriverResult::Success}; | ||
| 102 | TagFoundData tag_data{}; | ||
| 103 | |||
| 104 | if (result == DriverResult::Success) { | ||
| 105 | result = StartPolling(tag_data); | ||
| 106 | } | ||
| 107 | |||
| 108 | return result == DriverResult::Success; | ||
| 109 | } | ||
| 110 | |||
| 111 | DriverResult NfcProtocol::WaitUntilNfcIsReady() { | ||
| 112 | constexpr std::size_t timeout_limit = 10; | ||
| 113 | std::vector<u8> output; | ||
| 114 | std::size_t tries = 0; | ||
| 115 | |||
| 116 | do { | ||
| 117 | auto result = SendStartWaitingRecieveRequest(output); | ||
| 118 | |||
| 119 | if (result != DriverResult::Success) { | ||
| 120 | return result; | ||
| 121 | } | ||
| 122 | if (tries++ > timeout_limit) { | ||
| 123 | return DriverResult::Timeout; | ||
| 124 | } | ||
| 125 | } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[55] != 0x31 || | ||
| 126 | output[56] != 0x00); | ||
| 127 | |||
| 128 | return DriverResult::Success; | ||
| 129 | } | ||
| 130 | |||
| 131 | DriverResult NfcProtocol::StartPolling(TagFoundData& data) { | ||
| 132 | LOG_DEBUG(Input, "Start Polling for tag"); | ||
| 133 | constexpr std::size_t timeout_limit = 7; | ||
| 134 | std::vector<u8> output; | ||
| 135 | std::size_t tries = 0; | ||
| 136 | |||
| 137 | do { | ||
| 138 | const auto result = SendStartPollingRequest(output); | ||
| 139 | if (result != DriverResult::Success) { | ||
| 140 | return result; | ||
| 141 | } | ||
| 142 | if (tries++ > timeout_limit) { | ||
| 143 | return DriverResult::Timeout; | ||
| 144 | } | ||
| 145 | } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[56] != 0x09); | ||
| 146 | |||
| 147 | data.type = output[62]; | ||
| 148 | data.uuid.resize(output[64]); | ||
| 149 | memcpy(data.uuid.data(), output.data() + 65, data.uuid.size()); | ||
| 150 | |||
| 151 | return DriverResult::Success; | ||
| 152 | } | ||
| 153 | |||
| 154 | DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { | ||
| 155 | constexpr std::size_t timeout_limit = 10; | ||
| 156 | std::vector<u8> output; | ||
| 157 | std::size_t tries = 0; | ||
| 158 | |||
| 159 | std::string uuid_string; | ||
| 160 | for (auto& content : data.uuid) { | ||
| 161 | uuid_string += fmt::format(" {:02x}", content); | ||
| 162 | } | ||
| 163 | |||
| 164 | LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string); | ||
| 165 | |||
| 166 | tries = 0; | ||
| 167 | NFCPages ntag_pages = NFCPages::Block0; | ||
| 168 | // Read Tag data | ||
| 169 | while (true) { | ||
| 170 | auto result = SendReadAmiiboRequest(output, ntag_pages); | ||
| 171 | const auto mcu_report = static_cast<MCUReport>(output[49]); | ||
| 172 | const auto nfc_status = static_cast<NFCStatus>(output[56]); | ||
| 173 | |||
| 174 | if (result != DriverResult::Success) { | ||
| 175 | return result; | ||
| 176 | } | ||
| 177 | |||
| 178 | if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && | ||
| 179 | nfc_status == NFCStatus::TagLost) { | ||
| 180 | return DriverResult::ErrorReadingData; | ||
| 181 | } | ||
| 182 | |||
| 183 | if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07 && output[52] == 0x01) { | ||
| 184 | if (data.type != 2) { | ||
| 185 | continue; | ||
| 186 | } | ||
| 187 | switch (output[74]) { | ||
| 188 | case 0: | ||
| 189 | ntag_pages = NFCPages::Block135; | ||
| 190 | break; | ||
| 191 | case 3: | ||
| 192 | ntag_pages = NFCPages::Block45; | ||
| 193 | break; | ||
| 194 | case 4: | ||
| 195 | ntag_pages = NFCPages::Block231; | ||
| 196 | break; | ||
| 197 | default: | ||
| 198 | return DriverResult::ErrorReadingData; | ||
| 199 | } | ||
| 200 | continue; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { | ||
| 204 | // finished | ||
| 205 | SendStopPollingRequest(output); | ||
| 206 | return DriverResult::Success; | ||
| 207 | } | ||
| 208 | |||
| 209 | // Ignore other state reports | ||
| 210 | if (mcu_report == MCUReport::NFCState) { | ||
| 211 | continue; | ||
| 212 | } | ||
| 213 | |||
| 214 | if (tries++ > timeout_limit) { | ||
| 215 | return DriverResult::Timeout; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | return DriverResult::Success; | ||
| 220 | } | ||
| 221 | |||
| 222 | DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { | ||
| 223 | constexpr std::size_t timeout_limit = 10; | ||
| 224 | std::vector<u8> output; | ||
| 225 | std::size_t tries = 0; | ||
| 226 | |||
| 227 | NFCPages ntag_pages = NFCPages::Block135; | ||
| 228 | std::size_t ntag_buffer_pos = 0; | ||
| 229 | // Read Tag data | ||
| 230 | while (true) { | ||
| 231 | auto result = SendReadAmiiboRequest(output, ntag_pages); | ||
| 232 | const auto mcu_report = static_cast<MCUReport>(output[49]); | ||
| 233 | const auto nfc_status = static_cast<NFCStatus>(output[56]); | ||
| 234 | |||
| 235 | if (result != DriverResult::Success) { | ||
| 236 | return result; | ||
| 237 | } | ||
| 238 | |||
| 239 | if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && | ||
| 240 | nfc_status == NFCStatus::TagLost) { | ||
| 241 | return DriverResult::ErrorReadingData; | ||
| 242 | } | ||
| 243 | |||
| 244 | if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07) { | ||
| 245 | std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF; | ||
| 246 | if (output[52] == 0x01) { | ||
| 247 | memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, payload_size - 60); | ||
| 248 | ntag_buffer_pos += payload_size - 60; | ||
| 249 | } else { | ||
| 250 | memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size); | ||
| 251 | } | ||
| 252 | continue; | ||
| 253 | } | ||
| 254 | |||
| 255 | if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { | ||
| 256 | LOG_INFO(Input, "Finished reading amiibo"); | ||
| 257 | return DriverResult::Success; | ||
| 258 | } | ||
| 259 | |||
| 260 | // Ignore other state reports | ||
| 261 | if (mcu_report == MCUReport::NFCState) { | ||
| 262 | continue; | ||
| 263 | } | ||
| 264 | |||
| 265 | if (tries++ > timeout_limit) { | ||
| 266 | return DriverResult::Timeout; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | return DriverResult::Success; | ||
| 271 | } | ||
| 272 | |||
| 273 | DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) { | ||
| 274 | NFCRequestState request{ | ||
| 275 | .sub_command = MCUSubCommand::ReadDeviceMode, | ||
| 276 | .command_argument = NFCReadCommand::StartPolling, | ||
| 277 | .packet_id = 0x0, | ||
| 278 | .packet_flag = MCUPacketFlag::LastCommandPacket, | ||
| 279 | .data_length = sizeof(NFCPollingCommandData), | ||
| 280 | .nfc_polling = | ||
| 281 | { | ||
| 282 | .enable_mifare = 0x01, | ||
| 283 | .unknown_1 = 0x00, | ||
| 284 | .unknown_2 = 0x00, | ||
| 285 | .unknown_3 = 0x2c, | ||
| 286 | .unknown_4 = 0x01, | ||
| 287 | }, | ||
| 288 | .crc = {}, | ||
| 289 | }; | ||
| 290 | |||
| 291 | std::array<u8, sizeof(NFCRequestState)> request_data{}; | ||
| 292 | memcpy(request_data.data(), &request, sizeof(NFCRequestState)); | ||
| 293 | request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); | ||
| 294 | return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); | ||
| 295 | } | ||
| 296 | |||
| 297 | DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) { | ||
| 298 | NFCRequestState request{ | ||
| 299 | .sub_command = MCUSubCommand::ReadDeviceMode, | ||
| 300 | .command_argument = NFCReadCommand::StopPolling, | ||
| 301 | .packet_id = 0x0, | ||
| 302 | .packet_flag = MCUPacketFlag::LastCommandPacket, | ||
| 303 | .data_length = 0, | ||
| 304 | .raw_data = {}, | ||
| 305 | .crc = {}, | ||
| 306 | }; | ||
| 307 | |||
| 308 | std::array<u8, sizeof(NFCRequestState)> request_data{}; | ||
| 309 | memcpy(request_data.data(), &request, sizeof(NFCRequestState)); | ||
| 310 | request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); | ||
| 311 | return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); | ||
| 312 | } | ||
| 313 | |||
| 314 | DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output) { | ||
| 315 | NFCRequestState request{ | ||
| 316 | .sub_command = MCUSubCommand::ReadDeviceMode, | ||
| 317 | .command_argument = NFCReadCommand::StartWaitingRecieve, | ||
| 318 | .packet_id = 0x0, | ||
| 319 | .packet_flag = MCUPacketFlag::LastCommandPacket, | ||
| 320 | .data_length = 0, | ||
| 321 | .raw_data = {}, | ||
| 322 | .crc = {}, | ||
| 323 | }; | ||
| 324 | |||
| 325 | std::vector<u8> request_data(sizeof(NFCRequestState)); | ||
| 326 | memcpy(request_data.data(), &request, sizeof(NFCRequestState)); | ||
| 327 | request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); | ||
| 328 | return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); | ||
| 329 | } | ||
| 330 | |||
| 331 | DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages) { | ||
| 332 | NFCRequestState request{ | ||
| 333 | .sub_command = MCUSubCommand::ReadDeviceMode, | ||
| 334 | .command_argument = NFCReadCommand::Ntag, | ||
| 335 | .packet_id = 0x0, | ||
| 336 | .packet_flag = MCUPacketFlag::LastCommandPacket, | ||
| 337 | .data_length = sizeof(NFCReadCommandData), | ||
| 338 | .nfc_read = | ||
| 339 | { | ||
| 340 | .unknown = 0xd0, | ||
| 341 | .uuid_length = 0x07, | ||
| 342 | .unknown_2 = 0x00, | ||
| 343 | .uid = {}, | ||
| 344 | .tag_type = NFCTagType::AllTags, | ||
| 345 | .read_block = GetReadBlockCommand(ntag_pages), | ||
| 346 | }, | ||
| 347 | .crc = {}, | ||
| 348 | }; | ||
| 349 | |||
| 350 | std::array<u8, sizeof(NFCRequestState)> request_data{}; | ||
| 351 | memcpy(request_data.data(), &request, sizeof(NFCRequestState)); | ||
| 352 | request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); | ||
| 353 | return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); | ||
| 354 | } | ||
| 355 | |||
| 356 | NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const { | ||
| 357 | switch (pages) { | ||
| 358 | case NFCPages::Block0: | ||
| 359 | return { | ||
| 360 | .block_count = 1, | ||
| 361 | }; | ||
| 362 | case NFCPages::Block45: | ||
| 363 | return { | ||
| 364 | .block_count = 1, | ||
| 365 | .blocks = | ||
| 366 | { | ||
| 367 | NFCReadBlock{0x00, 0x2C}, | ||
| 368 | }, | ||
| 369 | }; | ||
| 370 | case NFCPages::Block135: | ||
| 371 | return { | ||
| 372 | .block_count = 3, | ||
| 373 | .blocks = | ||
| 374 | { | ||
| 375 | NFCReadBlock{0x00, 0x3b}, | ||
| 376 | {0x3c, 0x77}, | ||
| 377 | {0x78, 0x86}, | ||
| 378 | }, | ||
| 379 | }; | ||
| 380 | case NFCPages::Block231: | ||
| 381 | return { | ||
| 382 | .block_count = 4, | ||
| 383 | .blocks = | ||
| 384 | { | ||
| 385 | NFCReadBlock{0x00, 0x3b}, | ||
| 386 | {0x3c, 0x77}, | ||
| 387 | {0x78, 0x83}, | ||
| 388 | {0xb4, 0xe6}, | ||
| 389 | }, | ||
| 390 | }; | ||
| 391 | default: | ||
| 392 | return {}; | ||
| 393 | }; | ||
| 394 | } | ||
| 395 | |||
| 396 | bool NfcProtocol::IsEnabled() const { | ||
| 397 | return is_enabled; | ||
| 398 | } | ||
| 399 | |||
| 400 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h new file mode 100644 index 000000000..e63665aa9 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/nfc.h | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "input_common/helpers/joycon_protocol/common_protocol.h" | ||
| 14 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 15 | |||
| 16 | namespace InputCommon::Joycon { | ||
| 17 | |||
| 18 | class NfcProtocol final : private JoyconCommonProtocol { | ||
| 19 | public: | ||
| 20 | explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle); | ||
| 21 | |||
| 22 | DriverResult EnableNfc(); | ||
| 23 | |||
| 24 | DriverResult DisableNfc(); | ||
| 25 | |||
| 26 | DriverResult StartNFCPollingMode(); | ||
| 27 | |||
| 28 | DriverResult ScanAmiibo(std::vector<u8>& data); | ||
| 29 | |||
| 30 | bool HasAmiibo(); | ||
| 31 | |||
| 32 | bool IsEnabled() const; | ||
| 33 | |||
| 34 | private: | ||
| 35 | struct TagFoundData { | ||
| 36 | u8 type; | ||
| 37 | std::vector<u8> uuid; | ||
| 38 | }; | ||
| 39 | |||
| 40 | DriverResult WaitUntilNfcIsReady(); | ||
| 41 | |||
| 42 | DriverResult StartPolling(TagFoundData& data); | ||
| 43 | |||
| 44 | DriverResult ReadTag(const TagFoundData& data); | ||
| 45 | |||
| 46 | DriverResult GetAmiiboData(std::vector<u8>& data); | ||
| 47 | |||
| 48 | DriverResult SendStartPollingRequest(std::vector<u8>& output); | ||
| 49 | |||
| 50 | DriverResult SendStopPollingRequest(std::vector<u8>& output); | ||
| 51 | |||
| 52 | DriverResult SendStartWaitingRecieveRequest(std::vector<u8>& output); | ||
| 53 | |||
| 54 | DriverResult SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages); | ||
| 55 | |||
| 56 | NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; | ||
| 57 | |||
| 58 | bool is_enabled{}; | ||
| 59 | }; | ||
| 60 | |||
| 61 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp new file mode 100644 index 000000000..7f8e093fa --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/poller.cpp | |||
| @@ -0,0 +1,341 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "input_common/helpers/joycon_protocol/poller.h" | ||
| 6 | |||
| 7 | namespace InputCommon::Joycon { | ||
| 8 | |||
| 9 | JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_, | ||
| 10 | JoyStickCalibration right_stick_calibration_, | ||
| 11 | MotionCalibration motion_calibration_) | ||
| 12 | : device_type{device_type_}, left_stick_calibration{left_stick_calibration_}, | ||
| 13 | right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {} | ||
| 14 | |||
| 15 | void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) { | ||
| 16 | callbacks = std::move(callbacks_); | ||
| 17 | } | ||
| 18 | |||
| 19 | void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status, | ||
| 20 | const RingStatus& ring_status) { | ||
| 21 | InputReportActive data{}; | ||
| 22 | memcpy(&data, buffer.data(), sizeof(InputReportActive)); | ||
| 23 | |||
| 24 | switch (device_type) { | ||
| 25 | case Joycon::ControllerType::Left: | ||
| 26 | UpdateActiveLeftPadInput(data, motion_status); | ||
| 27 | break; | ||
| 28 | case Joycon::ControllerType::Right: | ||
| 29 | UpdateActiveRightPadInput(data, motion_status); | ||
| 30 | break; | ||
| 31 | case Joycon::ControllerType::Pro: | ||
| 32 | UpdateActiveProPadInput(data, motion_status); | ||
| 33 | break; | ||
| 34 | case Joycon::ControllerType::Grip: | ||
| 35 | case Joycon::ControllerType::Dual: | ||
| 36 | case Joycon::ControllerType::None: | ||
| 37 | break; | ||
| 38 | } | ||
| 39 | |||
| 40 | if (ring_status.is_enabled) { | ||
| 41 | UpdateRing(data.ring_input, ring_status); | ||
| 42 | } | ||
| 43 | |||
| 44 | callbacks.on_battery_data(data.battery_status); | ||
| 45 | } | ||
| 46 | |||
| 47 | void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) { | ||
| 48 | InputReportPassive data{}; | ||
| 49 | memcpy(&data, buffer.data(), sizeof(InputReportPassive)); | ||
| 50 | |||
| 51 | switch (device_type) { | ||
| 52 | case Joycon::ControllerType::Left: | ||
| 53 | UpdatePasiveLeftPadInput(data); | ||
| 54 | break; | ||
| 55 | case Joycon::ControllerType::Right: | ||
| 56 | UpdatePasiveRightPadInput(data); | ||
| 57 | break; | ||
| 58 | case Joycon::ControllerType::Pro: | ||
| 59 | UpdatePasiveProPadInput(data); | ||
| 60 | break; | ||
| 61 | case Joycon::ControllerType::Grip: | ||
| 62 | case Joycon::ControllerType::Dual: | ||
| 63 | case Joycon::ControllerType::None: | ||
| 64 | break; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) { | ||
| 69 | // This mode is compatible with the active mode | ||
| 70 | ReadActiveMode(buffer, motion_status, {}); | ||
| 71 | } | ||
| 72 | |||
| 73 | void JoyconPoller::UpdateColor(const Color& color) { | ||
| 74 | callbacks.on_color_data(color); | ||
| 75 | } | ||
| 76 | |||
| 77 | void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) { | ||
| 78 | callbacks.on_amiibo_data(amiibo_data); | ||
| 79 | } | ||
| 80 | |||
| 81 | void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { | ||
| 82 | callbacks.on_camera_data(camera_data, format); | ||
| 83 | } | ||
| 84 | |||
| 85 | void JoyconPoller::UpdateRing(s16 value, const RingStatus& ring_status) { | ||
| 86 | float normalized_value = static_cast<float>(value - ring_status.default_value); | ||
| 87 | if (normalized_value > 0) { | ||
| 88 | normalized_value = normalized_value / | ||
| 89 | static_cast<float>(ring_status.max_value - ring_status.default_value); | ||
| 90 | } | ||
| 91 | if (normalized_value < 0) { | ||
| 92 | normalized_value = normalized_value / | ||
| 93 | static_cast<float>(ring_status.default_value - ring_status.min_value); | ||
| 94 | } | ||
| 95 | callbacks.on_ring_data(normalized_value); | ||
| 96 | } | ||
| 97 | |||
| 98 | void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input, | ||
| 99 | const MotionStatus& motion_status) { | ||
| 100 | static constexpr std::array<Joycon::PadButton, 11> left_buttons{ | ||
| 101 | Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right, | ||
| 102 | Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR, | ||
| 103 | Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus, | ||
| 104 | Joycon::PadButton::Capture, Joycon::PadButton::StickL, | ||
| 105 | }; | ||
| 106 | |||
| 107 | const u32 raw_button = | ||
| 108 | static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16)); | ||
| 109 | for (std::size_t i = 0; i < left_buttons.size(); ++i) { | ||
| 110 | const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0; | ||
| 111 | const int button = static_cast<int>(left_buttons[i]); | ||
| 112 | callbacks.on_button_data(button, button_status); | ||
| 113 | } | ||
| 114 | |||
| 115 | const u16 raw_left_axis_x = | ||
| 116 | static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8)); | ||
| 117 | const u16 raw_left_axis_y = | ||
| 118 | static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4)); | ||
| 119 | const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x); | ||
| 120 | const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y); | ||
| 121 | callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x); | ||
| 122 | callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y); | ||
| 123 | |||
| 124 | if (motion_status.is_enabled) { | ||
| 125 | auto left_motion = GetMotionInput(input, motion_status); | ||
| 126 | // Rotate motion axis to the correct direction | ||
| 127 | left_motion.accel_y = -left_motion.accel_y; | ||
| 128 | left_motion.accel_z = -left_motion.accel_z; | ||
| 129 | left_motion.gyro_x = -left_motion.gyro_x; | ||
| 130 | callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input, | ||
| 135 | const MotionStatus& motion_status) { | ||
| 136 | static constexpr std::array<Joycon::PadButton, 11> right_buttons{ | ||
| 137 | Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B, | ||
| 138 | Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR, | ||
| 139 | Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus, | ||
| 140 | Joycon::PadButton::Home, Joycon::PadButton::StickR, | ||
| 141 | }; | ||
| 142 | |||
| 143 | const u32 raw_button = | ||
| 144 | static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16)); | ||
| 145 | for (std::size_t i = 0; i < right_buttons.size(); ++i) { | ||
| 146 | const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0; | ||
| 147 | const int button = static_cast<int>(right_buttons[i]); | ||
| 148 | callbacks.on_button_data(button, button_status); | ||
| 149 | } | ||
| 150 | |||
| 151 | const u16 raw_right_axis_x = | ||
| 152 | static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8)); | ||
| 153 | const u16 raw_right_axis_y = | ||
| 154 | static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4)); | ||
| 155 | const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x); | ||
| 156 | const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y); | ||
| 157 | callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x); | ||
| 158 | callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y); | ||
| 159 | |||
| 160 | if (motion_status.is_enabled) { | ||
| 161 | auto right_motion = GetMotionInput(input, motion_status); | ||
| 162 | // Rotate motion axis to the correct direction | ||
| 163 | right_motion.accel_x = -right_motion.accel_x; | ||
| 164 | right_motion.accel_y = -right_motion.accel_y; | ||
| 165 | right_motion.gyro_z = -right_motion.gyro_z; | ||
| 166 | callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input, | ||
| 171 | const MotionStatus& motion_status) { | ||
| 172 | static constexpr std::array<Joycon::PadButton, 18> pro_buttons{ | ||
| 173 | Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right, | ||
| 174 | Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL, | ||
| 175 | Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y, | ||
| 176 | Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A, | ||
| 177 | Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus, | ||
| 178 | Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR, | ||
| 179 | }; | ||
| 180 | |||
| 181 | const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) | | ||
| 182 | (input.button_input[1] << 16)); | ||
| 183 | for (std::size_t i = 0; i < pro_buttons.size(); ++i) { | ||
| 184 | const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0; | ||
| 185 | const int button = static_cast<int>(pro_buttons[i]); | ||
| 186 | callbacks.on_button_data(button, button_status); | ||
| 187 | } | ||
| 188 | |||
| 189 | const u16 raw_left_axis_x = | ||
| 190 | static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8)); | ||
| 191 | const u16 raw_left_axis_y = | ||
| 192 | static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4)); | ||
| 193 | const u16 raw_right_axis_x = | ||
| 194 | static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8)); | ||
| 195 | const u16 raw_right_axis_y = | ||
| 196 | static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4)); | ||
| 197 | |||
| 198 | const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x); | ||
| 199 | const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y); | ||
| 200 | const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x); | ||
| 201 | const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y); | ||
| 202 | callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x); | ||
| 203 | callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y); | ||
| 204 | callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x); | ||
| 205 | callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y); | ||
| 206 | |||
| 207 | if (motion_status.is_enabled) { | ||
| 208 | auto pro_motion = GetMotionInput(input, motion_status); | ||
| 209 | pro_motion.gyro_x = -pro_motion.gyro_x; | ||
| 210 | pro_motion.accel_y = -pro_motion.accel_y; | ||
| 211 | pro_motion.accel_z = -pro_motion.accel_z; | ||
| 212 | callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion); | ||
| 213 | callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion); | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) { | ||
| 218 | static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{ | ||
| 219 | Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, | ||
| 220 | Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, | ||
| 221 | Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, | ||
| 222 | Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, | ||
| 223 | Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture, | ||
| 224 | Joycon::PasivePadButton::StickL, | ||
| 225 | }; | ||
| 226 | |||
| 227 | for (auto left_button : left_buttons) { | ||
| 228 | const bool button_status = (input.button_input & static_cast<u32>(left_button)) != 0; | ||
| 229 | const int button = static_cast<int>(left_button); | ||
| 230 | callbacks.on_button_data(button, button_status); | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) { | ||
| 235 | static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{ | ||
| 236 | Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, | ||
| 237 | Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, | ||
| 238 | Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, | ||
| 239 | Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, | ||
| 240 | Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home, | ||
| 241 | Joycon::PasivePadButton::StickR, | ||
| 242 | }; | ||
| 243 | |||
| 244 | for (auto right_button : right_buttons) { | ||
| 245 | const bool button_status = (input.button_input & static_cast<u32>(right_button)) != 0; | ||
| 246 | const int button = static_cast<int>(right_button); | ||
| 247 | callbacks.on_button_data(button, button_status); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) { | ||
| 252 | static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{ | ||
| 253 | Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X, | ||
| 254 | Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y, | ||
| 255 | Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR, | ||
| 256 | Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR, | ||
| 257 | Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus, | ||
| 258 | Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home, | ||
| 259 | Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR, | ||
| 260 | }; | ||
| 261 | |||
| 262 | for (auto pro_button : pro_buttons) { | ||
| 263 | const bool button_status = (input.button_input & static_cast<u32>(pro_button)) != 0; | ||
| 264 | const int button = static_cast<int>(pro_button); | ||
| 265 | callbacks.on_button_data(button, button_status); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const { | ||
| 270 | const f32 value = static_cast<f32>(raw_value - calibration.center); | ||
| 271 | if (value > 0.0f) { | ||
| 272 | return value / calibration.max; | ||
| 273 | } | ||
| 274 | return value / calibration.min; | ||
| 275 | } | ||
| 276 | |||
| 277 | f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal, | ||
| 278 | AccelerometerSensitivity sensitivity) const { | ||
| 279 | const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4; | ||
| 280 | switch (sensitivity) { | ||
| 281 | case Joycon::AccelerometerSensitivity::G2: | ||
| 282 | return value / 4.0f; | ||
| 283 | case Joycon::AccelerometerSensitivity::G4: | ||
| 284 | return value / 2.0f; | ||
| 285 | case Joycon::AccelerometerSensitivity::G8: | ||
| 286 | return value; | ||
| 287 | case Joycon::AccelerometerSensitivity::G16: | ||
| 288 | return value * 2.0f; | ||
| 289 | } | ||
| 290 | return value; | ||
| 291 | } | ||
| 292 | |||
| 293 | f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal, | ||
| 294 | GyroSensitivity sensitivity) const { | ||
| 295 | const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f; | ||
| 296 | switch (sensitivity) { | ||
| 297 | case Joycon::GyroSensitivity::DPS250: | ||
| 298 | return value / 8.0f; | ||
| 299 | case Joycon::GyroSensitivity::DPS500: | ||
| 300 | return value / 4.0f; | ||
| 301 | case Joycon::GyroSensitivity::DPS1000: | ||
| 302 | return value / 2.0f; | ||
| 303 | case Joycon::GyroSensitivity::DPS2000: | ||
| 304 | return value; | ||
| 305 | } | ||
| 306 | return value; | ||
| 307 | } | ||
| 308 | |||
| 309 | s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis, | ||
| 310 | const InputReportActive& input) const { | ||
| 311 | return input.motion_input[(sensor * 3) + axis]; | ||
| 312 | } | ||
| 313 | |||
| 314 | MotionData JoyconPoller::GetMotionInput(const InputReportActive& input, | ||
| 315 | const MotionStatus& motion_status) const { | ||
| 316 | MotionData motion{}; | ||
| 317 | const auto& accel_cal = motion_calibration.accelerometer; | ||
| 318 | const auto& gyro_cal = motion_calibration.gyro; | ||
| 319 | const s16 raw_accel_x = input.motion_input[1]; | ||
| 320 | const s16 raw_accel_y = input.motion_input[0]; | ||
| 321 | const s16 raw_accel_z = input.motion_input[2]; | ||
| 322 | const s16 raw_gyro_x = input.motion_input[4]; | ||
| 323 | const s16 raw_gyro_y = input.motion_input[3]; | ||
| 324 | const s16 raw_gyro_z = input.motion_input[5]; | ||
| 325 | |||
| 326 | motion.delta_timestamp = motion_status.delta_time; | ||
| 327 | motion.accel_x = | ||
| 328 | GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity); | ||
| 329 | motion.accel_y = | ||
| 330 | GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity); | ||
| 331 | motion.accel_z = | ||
| 332 | GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity); | ||
| 333 | motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity); | ||
| 334 | motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity); | ||
| 335 | motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity); | ||
| 336 | |||
| 337 | // TODO(German77): Return all three samples data | ||
| 338 | return motion; | ||
| 339 | } | ||
| 340 | |||
| 341 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h new file mode 100644 index 000000000..354d41dad --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/poller.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <functional> | ||
| 12 | #include <span> | ||
| 13 | |||
| 14 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 15 | |||
| 16 | namespace InputCommon::Joycon { | ||
| 17 | |||
| 18 | // Handles input packages and triggers the corresponding input events | ||
| 19 | class JoyconPoller { | ||
| 20 | public: | ||
| 21 | JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_, | ||
| 22 | JoyStickCalibration right_stick_calibration_, | ||
| 23 | MotionCalibration motion_calibration_); | ||
| 24 | |||
| 25 | void SetCallbacks(const Joycon::JoyconCallbacks& callbacks_); | ||
| 26 | |||
| 27 | /// Handles data from passive packages | ||
| 28 | void ReadPassiveMode(std::span<u8> buffer); | ||
| 29 | |||
| 30 | /// Handles data from active packages | ||
| 31 | void ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status, | ||
| 32 | const RingStatus& ring_status); | ||
| 33 | |||
| 34 | /// Handles data from nfc or ir packages | ||
| 35 | void ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status); | ||
| 36 | |||
| 37 | void UpdateColor(const Color& color); | ||
| 38 | void UpdateRing(s16 value, const RingStatus& ring_status); | ||
| 39 | void UpdateAmiibo(const std::vector<u8>& amiibo_data); | ||
| 40 | void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format); | ||
| 41 | |||
| 42 | private: | ||
| 43 | void UpdateActiveLeftPadInput(const InputReportActive& input, | ||
| 44 | const MotionStatus& motion_status); | ||
| 45 | void UpdateActiveRightPadInput(const InputReportActive& input, | ||
| 46 | const MotionStatus& motion_status); | ||
| 47 | void UpdateActiveProPadInput(const InputReportActive& input, const MotionStatus& motion_status); | ||
| 48 | |||
| 49 | void UpdatePasiveLeftPadInput(const InputReportPassive& buffer); | ||
| 50 | void UpdatePasiveRightPadInput(const InputReportPassive& buffer); | ||
| 51 | void UpdatePasiveProPadInput(const InputReportPassive& buffer); | ||
| 52 | |||
| 53 | /// Returns a calibrated joystick axis from raw axis data | ||
| 54 | f32 GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const; | ||
| 55 | |||
| 56 | /// Returns a calibrated accelerometer axis from raw motion data | ||
| 57 | f32 GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal, | ||
| 58 | AccelerometerSensitivity sensitivity) const; | ||
| 59 | |||
| 60 | /// Returns a calibrated gyro axis from raw motion data | ||
| 61 | f32 GetGyroValue(s16 raw_value, const MotionSensorCalibration& cal, | ||
| 62 | GyroSensitivity sensitivity) const; | ||
| 63 | |||
| 64 | /// Returns a raw motion value from a buffer | ||
| 65 | s16 GetRawIMUValues(size_t sensor, size_t axis, const InputReportActive& input) const; | ||
| 66 | |||
| 67 | /// Returns motion data from a buffer | ||
| 68 | MotionData GetMotionInput(const InputReportActive& input, | ||
| 69 | const MotionStatus& motion_status) const; | ||
| 70 | |||
| 71 | ControllerType device_type{}; | ||
| 72 | |||
| 73 | // Device calibration | ||
| 74 | JoyStickCalibration left_stick_calibration{}; | ||
| 75 | JoyStickCalibration right_stick_calibration{}; | ||
| 76 | MotionCalibration motion_calibration{}; | ||
| 77 | |||
| 78 | Joycon::JoyconCallbacks callbacks{}; | ||
| 79 | }; | ||
| 80 | |||
| 81 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp new file mode 100644 index 000000000..12f81309e --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "input_common/helpers/joycon_protocol/ringcon.h" | ||
| 6 | |||
| 7 | namespace InputCommon::Joycon { | ||
| 8 | |||
| 9 | RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle) | ||
| 10 | : JoyconCommonProtocol(std::move(handle)) {} | ||
| 11 | |||
| 12 | DriverResult RingConProtocol::EnableRingCon() { | ||
| 13 | LOG_DEBUG(Input, "Enable Ringcon"); | ||
| 14 | ScopedSetBlocking sb(this); | ||
| 15 | DriverResult result{DriverResult::Success}; | ||
| 16 | |||
| 17 | if (result == DriverResult::Success) { | ||
| 18 | result = SetReportMode(ReportMode::STANDARD_FULL_60HZ); | ||
| 19 | } | ||
| 20 | if (result == DriverResult::Success) { | ||
| 21 | result = EnableMCU(true); | ||
| 22 | } | ||
| 23 | if (result == DriverResult::Success) { | ||
| 24 | const MCUConfig config{ | ||
| 25 | .command = MCUCommand::ConfigureMCU, | ||
| 26 | .sub_command = MCUSubCommand::SetDeviceMode, | ||
| 27 | .mode = MCUMode::Standby, | ||
| 28 | .crc = {}, | ||
| 29 | }; | ||
| 30 | result = ConfigureMCU(config); | ||
| 31 | } | ||
| 32 | |||
| 33 | return result; | ||
| 34 | } | ||
| 35 | |||
| 36 | DriverResult RingConProtocol::DisableRingCon() { | ||
| 37 | LOG_DEBUG(Input, "Disable RingCon"); | ||
| 38 | ScopedSetBlocking sb(this); | ||
| 39 | DriverResult result{DriverResult::Success}; | ||
| 40 | |||
| 41 | if (result == DriverResult::Success) { | ||
| 42 | result = EnableMCU(false); | ||
| 43 | } | ||
| 44 | |||
| 45 | is_enabled = false; | ||
| 46 | |||
| 47 | return result; | ||
| 48 | } | ||
| 49 | |||
| 50 | DriverResult RingConProtocol::StartRingconPolling() { | ||
| 51 | LOG_DEBUG(Input, "Enable Ringcon"); | ||
| 52 | ScopedSetBlocking sb(this); | ||
| 53 | DriverResult result{DriverResult::Success}; | ||
| 54 | bool is_connected = false; | ||
| 55 | |||
| 56 | if (result == DriverResult::Success) { | ||
| 57 | result = IsRingConnected(is_connected); | ||
| 58 | } | ||
| 59 | if (result == DriverResult::Success && is_connected) { | ||
| 60 | LOG_INFO(Input, "Ringcon detected"); | ||
| 61 | result = ConfigureRing(); | ||
| 62 | } | ||
| 63 | if (result == DriverResult::Success) { | ||
| 64 | is_enabled = true; | ||
| 65 | } | ||
| 66 | |||
| 67 | return result; | ||
| 68 | } | ||
| 69 | |||
| 70 | DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { | ||
| 71 | LOG_DEBUG(Input, "IsRingConnected"); | ||
| 72 | constexpr std::size_t max_tries = 28; | ||
| 73 | constexpr u8 ring_controller_id = 0x20; | ||
| 74 | std::vector<u8> output; | ||
| 75 | std::size_t tries = 0; | ||
| 76 | is_connected = false; | ||
| 77 | |||
| 78 | do { | ||
| 79 | std::array<u8, 1> empty_data{}; | ||
| 80 | const auto result = SendSubCommand(SubCommand::UNKNOWN_RINGCON, empty_data, output); | ||
| 81 | |||
| 82 | if (result != DriverResult::Success) { | ||
| 83 | return result; | ||
| 84 | } | ||
| 85 | |||
| 86 | if (tries++ >= max_tries) { | ||
| 87 | return DriverResult::NoDeviceDetected; | ||
| 88 | } | ||
| 89 | } while (output[16] != ring_controller_id); | ||
| 90 | |||
| 91 | is_connected = true; | ||
| 92 | return DriverResult::Success; | ||
| 93 | } | ||
| 94 | |||
| 95 | DriverResult RingConProtocol::ConfigureRing() { | ||
| 96 | LOG_DEBUG(Input, "ConfigureRing"); | ||
| 97 | |||
| 98 | static constexpr std::array<u8, 37> ring_config{ | ||
| 99 | 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, 0xED, 0x34, 0x36, | ||
| 100 | 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, | ||
| 101 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36}; | ||
| 102 | |||
| 103 | const DriverResult result = SendSubCommand(SubCommand::UNKNOWN_RINGCON3, ring_config); | ||
| 104 | |||
| 105 | if (result != DriverResult::Success) { | ||
| 106 | return result; | ||
| 107 | } | ||
| 108 | |||
| 109 | static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02}; | ||
| 110 | return SendSubCommand(SubCommand::UNKNOWN_RINGCON2, ringcon_data); | ||
| 111 | } | ||
| 112 | |||
| 113 | bool RingConProtocol::IsEnabled() const { | ||
| 114 | return is_enabled; | ||
| 115 | } | ||
| 116 | |||
| 117 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.h b/src/input_common/helpers/joycon_protocol/ringcon.h new file mode 100644 index 000000000..6e858f3fc --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/ringcon.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "input_common/helpers/joycon_protocol/common_protocol.h" | ||
| 14 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 15 | |||
| 16 | namespace InputCommon::Joycon { | ||
| 17 | |||
| 18 | class RingConProtocol final : private JoyconCommonProtocol { | ||
| 19 | public: | ||
| 20 | explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle); | ||
| 21 | |||
| 22 | DriverResult EnableRingCon(); | ||
| 23 | |||
| 24 | DriverResult DisableRingCon(); | ||
| 25 | |||
| 26 | DriverResult StartRingconPolling(); | ||
| 27 | |||
| 28 | bool IsEnabled() const; | ||
| 29 | |||
| 30 | private: | ||
| 31 | DriverResult IsRingConnected(bool& is_connected); | ||
| 32 | |||
| 33 | DriverResult ConfigureRing(); | ||
| 34 | |||
| 35 | bool is_enabled{}; | ||
| 36 | }; | ||
| 37 | |||
| 38 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp new file mode 100644 index 000000000..63b60c946 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/rumble.cpp | |||
| @@ -0,0 +1,299 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <cmath> | ||
| 6 | |||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "input_common/helpers/joycon_protocol/rumble.h" | ||
| 9 | |||
| 10 | namespace InputCommon::Joycon { | ||
| 11 | |||
| 12 | RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle) | ||
| 13 | : JoyconCommonProtocol(std::move(handle)) {} | ||
| 14 | |||
| 15 | DriverResult RumbleProtocol::EnableRumble(bool is_enabled) { | ||
| 16 | LOG_DEBUG(Input, "Enable Rumble"); | ||
| 17 | ScopedSetBlocking sb(this); | ||
| 18 | const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)}; | ||
| 19 | return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer); | ||
| 20 | } | ||
| 21 | |||
| 22 | DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) { | ||
| 23 | std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{}; | ||
| 24 | |||
| 25 | if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) { | ||
| 26 | return SendVibrationReport(DefaultVibrationBuffer); | ||
| 27 | } | ||
| 28 | |||
| 29 | // Protect joycons from damage from strong vibrations | ||
| 30 | const f32 clamp_amplitude = | ||
| 31 | 1.0f / std::max(1.0f, vibration.high_amplitude + vibration.low_amplitude); | ||
| 32 | |||
| 33 | const u16 encoded_high_frequency = EncodeHighFrequency(vibration.high_frequency); | ||
| 34 | const u8 encoded_high_amplitude = | ||
| 35 | EncodeHighAmplitude(vibration.high_amplitude * clamp_amplitude); | ||
| 36 | const u8 encoded_low_frequency = EncodeLowFrequency(vibration.low_frequency); | ||
| 37 | const u16 encoded_low_amplitude = EncodeLowAmplitude(vibration.low_amplitude * clamp_amplitude); | ||
| 38 | |||
| 39 | buffer[0] = static_cast<u8>(encoded_high_frequency & 0xFF); | ||
| 40 | buffer[1] = static_cast<u8>(encoded_high_amplitude | ((encoded_high_frequency >> 8) & 0x01)); | ||
| 41 | buffer[2] = static_cast<u8>(encoded_low_frequency | ((encoded_low_amplitude >> 8) & 0x80)); | ||
| 42 | buffer[3] = static_cast<u8>(encoded_low_amplitude & 0xFF); | ||
| 43 | |||
| 44 | // Duplicate rumble for now | ||
| 45 | buffer[4] = buffer[0]; | ||
| 46 | buffer[5] = buffer[1]; | ||
| 47 | buffer[6] = buffer[2]; | ||
| 48 | buffer[7] = buffer[3]; | ||
| 49 | |||
| 50 | return SendVibrationReport(buffer); | ||
| 51 | } | ||
| 52 | |||
| 53 | u16 RumbleProtocol::EncodeHighFrequency(f32 frequency) const { | ||
| 54 | const u8 new_frequency = | ||
| 55 | static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f)); | ||
| 56 | return static_cast<u16>((new_frequency - 0x60) * 4); | ||
| 57 | } | ||
| 58 | |||
| 59 | u8 RumbleProtocol::EncodeLowFrequency(f32 frequency) const { | ||
| 60 | const u8 new_frequency = | ||
| 61 | static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f)); | ||
| 62 | return static_cast<u8>(new_frequency - 0x40); | ||
| 63 | } | ||
| 64 | |||
| 65 | u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const { | ||
| 66 | // More information about these values can be found here: | ||
| 67 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md | ||
| 68 | |||
| 69 | static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ | ||
| 70 | std::pair<f32, int>{0.0f, 0x0}, | ||
| 71 | {0.01f, 0x2}, | ||
| 72 | {0.012f, 0x4}, | ||
| 73 | {0.014f, 0x6}, | ||
| 74 | {0.017f, 0x8}, | ||
| 75 | {0.02f, 0x0a}, | ||
| 76 | {0.024f, 0x0c}, | ||
| 77 | {0.028f, 0x0e}, | ||
| 78 | {0.033f, 0x10}, | ||
| 79 | {0.04f, 0x12}, | ||
| 80 | {0.047f, 0x14}, | ||
| 81 | {0.056f, 0x16}, | ||
| 82 | {0.067f, 0x18}, | ||
| 83 | {0.08f, 0x1a}, | ||
| 84 | {0.095f, 0x1c}, | ||
| 85 | {0.112f, 0x1e}, | ||
| 86 | {0.117f, 0x20}, | ||
| 87 | {0.123f, 0x22}, | ||
| 88 | {0.128f, 0x24}, | ||
| 89 | {0.134f, 0x26}, | ||
| 90 | {0.14f, 0x28}, | ||
| 91 | {0.146f, 0x2a}, | ||
| 92 | {0.152f, 0x2c}, | ||
| 93 | {0.159f, 0x2e}, | ||
| 94 | {0.166f, 0x30}, | ||
| 95 | {0.173f, 0x32}, | ||
| 96 | {0.181f, 0x34}, | ||
| 97 | {0.189f, 0x36}, | ||
| 98 | {0.198f, 0x38}, | ||
| 99 | {0.206f, 0x3a}, | ||
| 100 | {0.215f, 0x3c}, | ||
| 101 | {0.225f, 0x3e}, | ||
| 102 | {0.23f, 0x40}, | ||
| 103 | {0.235f, 0x42}, | ||
| 104 | {0.24f, 0x44}, | ||
| 105 | {0.245f, 0x46}, | ||
| 106 | {0.251f, 0x48}, | ||
| 107 | {0.256f, 0x4a}, | ||
| 108 | {0.262f, 0x4c}, | ||
| 109 | {0.268f, 0x4e}, | ||
| 110 | {0.273f, 0x50}, | ||
| 111 | {0.279f, 0x52}, | ||
| 112 | {0.286f, 0x54}, | ||
| 113 | {0.292f, 0x56}, | ||
| 114 | {0.298f, 0x58}, | ||
| 115 | {0.305f, 0x5a}, | ||
| 116 | {0.311f, 0x5c}, | ||
| 117 | {0.318f, 0x5e}, | ||
| 118 | {0.325f, 0x60}, | ||
| 119 | {0.332f, 0x62}, | ||
| 120 | {0.34f, 0x64}, | ||
| 121 | {0.347f, 0x66}, | ||
| 122 | {0.355f, 0x68}, | ||
| 123 | {0.362f, 0x6a}, | ||
| 124 | {0.37f, 0x6c}, | ||
| 125 | {0.378f, 0x6e}, | ||
| 126 | {0.387f, 0x70}, | ||
| 127 | {0.395f, 0x72}, | ||
| 128 | {0.404f, 0x74}, | ||
| 129 | {0.413f, 0x76}, | ||
| 130 | {0.422f, 0x78}, | ||
| 131 | {0.431f, 0x7a}, | ||
| 132 | {0.44f, 0x7c}, | ||
| 133 | {0.45f, 0x7e}, | ||
| 134 | {0.46f, 0x80}, | ||
| 135 | {0.47f, 0x82}, | ||
| 136 | {0.48f, 0x84}, | ||
| 137 | {0.491f, 0x86}, | ||
| 138 | {0.501f, 0x88}, | ||
| 139 | {0.512f, 0x8a}, | ||
| 140 | {0.524f, 0x8c}, | ||
| 141 | {0.535f, 0x8e}, | ||
| 142 | {0.547f, 0x90}, | ||
| 143 | {0.559f, 0x92}, | ||
| 144 | {0.571f, 0x94}, | ||
| 145 | {0.584f, 0x96}, | ||
| 146 | {0.596f, 0x98}, | ||
| 147 | {0.609f, 0x9a}, | ||
| 148 | {0.623f, 0x9c}, | ||
| 149 | {0.636f, 0x9e}, | ||
| 150 | {0.65f, 0xa0}, | ||
| 151 | {0.665f, 0xa2}, | ||
| 152 | {0.679f, 0xa4}, | ||
| 153 | {0.694f, 0xa6}, | ||
| 154 | {0.709f, 0xa8}, | ||
| 155 | {0.725f, 0xaa}, | ||
| 156 | {0.741f, 0xac}, | ||
| 157 | {0.757f, 0xae}, | ||
| 158 | {0.773f, 0xb0}, | ||
| 159 | {0.79f, 0xb2}, | ||
| 160 | {0.808f, 0xb4}, | ||
| 161 | {0.825f, 0xb6}, | ||
| 162 | {0.843f, 0xb8}, | ||
| 163 | {0.862f, 0xba}, | ||
| 164 | {0.881f, 0xbc}, | ||
| 165 | {0.9f, 0xbe}, | ||
| 166 | {0.92f, 0xc0}, | ||
| 167 | {0.94f, 0xc2}, | ||
| 168 | {0.96f, 0xc4}, | ||
| 169 | {0.981f, 0xc6}, | ||
| 170 | {1.003f, 0xc8}, | ||
| 171 | }; | ||
| 172 | |||
| 173 | for (const auto& [amplitude_value, code] : high_fequency_amplitude) { | ||
| 174 | if (amplitude <= amplitude_value) { | ||
| 175 | return static_cast<u8>(code); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | return static_cast<u8>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second); | ||
| 180 | } | ||
| 181 | |||
| 182 | u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const { | ||
| 183 | // More information about these values can be found here: | ||
| 184 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md | ||
| 185 | |||
| 186 | static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ | ||
| 187 | std::pair<f32, int>{0.0f, 0x0040}, | ||
| 188 | {0.01f, 0x8040}, | ||
| 189 | {0.012f, 0x0041}, | ||
| 190 | {0.014f, 0x8041}, | ||
| 191 | {0.017f, 0x0042}, | ||
| 192 | {0.02f, 0x8042}, | ||
| 193 | {0.024f, 0x0043}, | ||
| 194 | {0.028f, 0x8043}, | ||
| 195 | {0.033f, 0x0044}, | ||
| 196 | {0.04f, 0x8044}, | ||
| 197 | {0.047f, 0x0045}, | ||
| 198 | {0.056f, 0x8045}, | ||
| 199 | {0.067f, 0x0046}, | ||
| 200 | {0.08f, 0x8046}, | ||
| 201 | {0.095f, 0x0047}, | ||
| 202 | {0.112f, 0x8047}, | ||
| 203 | {0.117f, 0x0048}, | ||
| 204 | {0.123f, 0x8048}, | ||
| 205 | {0.128f, 0x0049}, | ||
| 206 | {0.134f, 0x8049}, | ||
| 207 | {0.14f, 0x004a}, | ||
| 208 | {0.146f, 0x804a}, | ||
| 209 | {0.152f, 0x004b}, | ||
| 210 | {0.159f, 0x804b}, | ||
| 211 | {0.166f, 0x004c}, | ||
| 212 | {0.173f, 0x804c}, | ||
| 213 | {0.181f, 0x004d}, | ||
| 214 | {0.189f, 0x804d}, | ||
| 215 | {0.198f, 0x004e}, | ||
| 216 | {0.206f, 0x804e}, | ||
| 217 | {0.215f, 0x004f}, | ||
| 218 | {0.225f, 0x804f}, | ||
| 219 | {0.23f, 0x0050}, | ||
| 220 | {0.235f, 0x8050}, | ||
| 221 | {0.24f, 0x0051}, | ||
| 222 | {0.245f, 0x8051}, | ||
| 223 | {0.251f, 0x0052}, | ||
| 224 | {0.256f, 0x8052}, | ||
| 225 | {0.262f, 0x0053}, | ||
| 226 | {0.268f, 0x8053}, | ||
| 227 | {0.273f, 0x0054}, | ||
| 228 | {0.279f, 0x8054}, | ||
| 229 | {0.286f, 0x0055}, | ||
| 230 | {0.292f, 0x8055}, | ||
| 231 | {0.298f, 0x0056}, | ||
| 232 | {0.305f, 0x8056}, | ||
| 233 | {0.311f, 0x0057}, | ||
| 234 | {0.318f, 0x8057}, | ||
| 235 | {0.325f, 0x0058}, | ||
| 236 | {0.332f, 0x8058}, | ||
| 237 | {0.34f, 0x0059}, | ||
| 238 | {0.347f, 0x8059}, | ||
| 239 | {0.355f, 0x005a}, | ||
| 240 | {0.362f, 0x805a}, | ||
| 241 | {0.37f, 0x005b}, | ||
| 242 | {0.378f, 0x805b}, | ||
| 243 | {0.387f, 0x005c}, | ||
| 244 | {0.395f, 0x805c}, | ||
| 245 | {0.404f, 0x005d}, | ||
| 246 | {0.413f, 0x805d}, | ||
| 247 | {0.422f, 0x005e}, | ||
| 248 | {0.431f, 0x805e}, | ||
| 249 | {0.44f, 0x005f}, | ||
| 250 | {0.45f, 0x805f}, | ||
| 251 | {0.46f, 0x0060}, | ||
| 252 | {0.47f, 0x8060}, | ||
| 253 | {0.48f, 0x0061}, | ||
| 254 | {0.491f, 0x8061}, | ||
| 255 | {0.501f, 0x0062}, | ||
| 256 | {0.512f, 0x8062}, | ||
| 257 | {0.524f, 0x0063}, | ||
| 258 | {0.535f, 0x8063}, | ||
| 259 | {0.547f, 0x0064}, | ||
| 260 | {0.559f, 0x8064}, | ||
| 261 | {0.571f, 0x0065}, | ||
| 262 | {0.584f, 0x8065}, | ||
| 263 | {0.596f, 0x0066}, | ||
| 264 | {0.609f, 0x8066}, | ||
| 265 | {0.623f, 0x0067}, | ||
| 266 | {0.636f, 0x8067}, | ||
| 267 | {0.65f, 0x0068}, | ||
| 268 | {0.665f, 0x8068}, | ||
| 269 | {0.679f, 0x0069}, | ||
| 270 | {0.694f, 0x8069}, | ||
| 271 | {0.709f, 0x006a}, | ||
| 272 | {0.725f, 0x806a}, | ||
| 273 | {0.741f, 0x006b}, | ||
| 274 | {0.757f, 0x806b}, | ||
| 275 | {0.773f, 0x006c}, | ||
| 276 | {0.79f, 0x806c}, | ||
| 277 | {0.808f, 0x006d}, | ||
| 278 | {0.825f, 0x806d}, | ||
| 279 | {0.843f, 0x006e}, | ||
| 280 | {0.862f, 0x806e}, | ||
| 281 | {0.881f, 0x006f}, | ||
| 282 | {0.9f, 0x806f}, | ||
| 283 | {0.92f, 0x0070}, | ||
| 284 | {0.94f, 0x8070}, | ||
| 285 | {0.96f, 0x0071}, | ||
| 286 | {0.981f, 0x8071}, | ||
| 287 | {1.003f, 0x0072}, | ||
| 288 | }; | ||
| 289 | |||
| 290 | for (const auto& [amplitude_value, code] : high_fequency_amplitude) { | ||
| 291 | if (amplitude <= amplitude_value) { | ||
| 292 | return static_cast<u16>(code); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | return static_cast<u16>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second); | ||
| 297 | } | ||
| 298 | |||
| 299 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/joycon_protocol/rumble.h b/src/input_common/helpers/joycon_protocol/rumble.h new file mode 100644 index 000000000..6c12b7925 --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/rumble.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse | ||
| 5 | // engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c | ||
| 6 | // https://github.com/CTCaer/jc_toolkit | ||
| 7 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | |||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "input_common/helpers/joycon_protocol/common_protocol.h" | ||
| 14 | #include "input_common/helpers/joycon_protocol/joycon_types.h" | ||
| 15 | |||
| 16 | namespace InputCommon::Joycon { | ||
| 17 | |||
| 18 | class RumbleProtocol final : private JoyconCommonProtocol { | ||
| 19 | public: | ||
| 20 | explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle); | ||
| 21 | |||
| 22 | DriverResult EnableRumble(bool is_enabled); | ||
| 23 | |||
| 24 | DriverResult SendVibration(const VibrationValue& vibration); | ||
| 25 | |||
| 26 | private: | ||
| 27 | u16 EncodeHighFrequency(f32 frequency) const; | ||
| 28 | u8 EncodeLowFrequency(f32 frequency) const; | ||
| 29 | u8 EncodeHighAmplitude(f32 amplitude) const; | ||
| 30 | u16 EncodeLowAmplitude(f32 amplitude) const; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace InputCommon::Joycon | ||
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp index f3a0b3419..096c23b07 100644 --- a/src/input_common/helpers/stick_from_buttons.cpp +++ b/src/input_common/helpers/stick_from_buttons.cpp | |||
| @@ -11,6 +11,11 @@ namespace InputCommon { | |||
| 11 | 11 | ||
| 12 | class Stick final : public Common::Input::InputDevice { | 12 | class Stick final : public Common::Input::InputDevice { |
| 13 | public: | 13 | public: |
| 14 | // Some games such as EARTH DEFENSE FORCE: WORLD BROTHERS | ||
| 15 | // do not play nicely with the theoretical maximum range. | ||
| 16 | // Using a value one lower from the maximum emulates real stick behavior. | ||
| 17 | static constexpr float MAX_RANGE = 32766.0f / 32767.0f; | ||
| 18 | |||
| 14 | using Button = std::unique_ptr<Common::Input::InputDevice>; | 19 | using Button = std::unique_ptr<Common::Input::InputDevice>; |
| 15 | 20 | ||
| 16 | Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_, | 21 | Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_, |
| @@ -196,7 +201,7 @@ public: | |||
| 196 | } | 201 | } |
| 197 | 202 | ||
| 198 | void UpdateStatus() { | 203 | void UpdateStatus() { |
| 199 | const float coef = modifier_status.value ? modifier_scale : 1.0f; | 204 | const float coef = modifier_status.value ? modifier_scale : MAX_RANGE; |
| 200 | 205 | ||
| 201 | bool r = right_status; | 206 | bool r = right_status; |
| 202 | bool l = left_status; | 207 | bool l = left_status; |
| @@ -290,7 +295,7 @@ public: | |||
| 290 | if (down_status) { | 295 | if (down_status) { |
| 291 | --y; | 296 | --y; |
| 292 | } | 297 | } |
| 293 | const float coef = modifier_status.value ? modifier_scale : 1.0f; | 298 | const float coef = modifier_status.value ? modifier_scale : MAX_RANGE; |
| 294 | status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); | 299 | status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); |
| 295 | status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); | 300 | status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); |
| 296 | return status; | 301 | return status; |
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index 61cfd0911..91aa96aa7 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp | |||
| @@ -79,6 +79,17 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat | |||
| 79 | TriggerOnBatteryChange(identifier, value); | 79 | TriggerOnBatteryChange(identifier, value); |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | void InputEngine::SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value) { | ||
| 83 | { | ||
| 84 | std::scoped_lock lock{mutex}; | ||
| 85 | ControllerData& controller = controller_list.at(identifier); | ||
| 86 | if (!configuring) { | ||
| 87 | controller.color = value; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | TriggerOnColorChange(identifier, value); | ||
| 91 | } | ||
| 92 | |||
| 82 | void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { | 93 | void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { |
| 83 | { | 94 | { |
| 84 | std::scoped_lock lock{mutex}; | 95 | std::scoped_lock lock{mutex}; |
| @@ -176,6 +187,18 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif | |||
| 176 | return controller.battery; | 187 | return controller.battery; |
| 177 | } | 188 | } |
| 178 | 189 | ||
| 190 | Common::Input::BodyColorStatus InputEngine::GetColor(const PadIdentifier& identifier) const { | ||
| 191 | std::scoped_lock lock{mutex}; | ||
| 192 | const auto controller_iter = controller_list.find(identifier); | ||
| 193 | if (controller_iter == controller_list.cend()) { | ||
| 194 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), | ||
| 195 | identifier.pad, identifier.port); | ||
| 196 | return {}; | ||
| 197 | } | ||
| 198 | const ControllerData& controller = controller_iter->second; | ||
| 199 | return controller.color; | ||
| 200 | } | ||
| 201 | |||
| 179 | BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { | 202 | BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { |
| 180 | std::scoped_lock lock{mutex}; | 203 | std::scoped_lock lock{mutex}; |
| 181 | const auto controller_iter = controller_list.find(identifier); | 204 | const auto controller_iter = controller_list.find(identifier); |
| @@ -328,6 +351,20 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, | |||
| 328 | } | 351 | } |
| 329 | } | 352 | } |
| 330 | 353 | ||
| 354 | void InputEngine::TriggerOnColorChange(const PadIdentifier& identifier, | ||
| 355 | [[maybe_unused]] Common::Input::BodyColorStatus value) { | ||
| 356 | std::scoped_lock lock{mutex_callback}; | ||
| 357 | for (const auto& poller_pair : callback_list) { | ||
| 358 | const InputIdentifier& poller = poller_pair.second; | ||
| 359 | if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Color, 0)) { | ||
| 360 | continue; | ||
| 361 | } | ||
| 362 | if (poller.callback.on_change) { | ||
| 363 | poller.callback.on_change(); | ||
| 364 | } | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 331 | void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, | 368 | void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, |
| 332 | const BasicMotion& value) { | 369 | const BasicMotion& value) { |
| 333 | std::scoped_lock lock{mutex_callback}; | 370 | std::scoped_lock lock{mutex_callback}; |
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 6cbcf5207..50b5a3dc8 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h | |||
| @@ -40,6 +40,7 @@ enum class EngineInputType { | |||
| 40 | Battery, | 40 | Battery, |
| 41 | Button, | 41 | Button, |
| 42 | Camera, | 42 | Camera, |
| 43 | Color, | ||
| 43 | HatButton, | 44 | HatButton, |
| 44 | Motion, | 45 | Motion, |
| 45 | Nfc, | 46 | Nfc, |
| @@ -104,14 +105,17 @@ public: | |||
| 104 | void EndConfiguration(); | 105 | void EndConfiguration(); |
| 105 | 106 | ||
| 106 | // Sets a led pattern for a controller | 107 | // Sets a led pattern for a controller |
| 107 | virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier, | 108 | virtual Common::Input::DriverResult SetLeds( |
| 108 | [[maybe_unused]] const Common::Input::LedStatus& led_status) {} | 109 | [[maybe_unused]] const PadIdentifier& identifier, |
| 110 | [[maybe_unused]] const Common::Input::LedStatus& led_status) { | ||
| 111 | return Common::Input::DriverResult::NotSupported; | ||
| 112 | } | ||
| 109 | 113 | ||
| 110 | // Sets rumble to a controller | 114 | // Sets rumble to a controller |
| 111 | virtual Common::Input::VibrationError SetVibration( | 115 | virtual Common::Input::DriverResult SetVibration( |
| 112 | [[maybe_unused]] const PadIdentifier& identifier, | 116 | [[maybe_unused]] const PadIdentifier& identifier, |
| 113 | [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { | 117 | [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { |
| 114 | return Common::Input::VibrationError::NotSupported; | 118 | return Common::Input::DriverResult::NotSupported; |
| 115 | } | 119 | } |
| 116 | 120 | ||
| 117 | // Returns true if device supports vibrations | 121 | // Returns true if device supports vibrations |
| @@ -120,17 +124,17 @@ public: | |||
| 120 | } | 124 | } |
| 121 | 125 | ||
| 122 | // Sets polling mode to a controller | 126 | // Sets polling mode to a controller |
| 123 | virtual Common::Input::PollingError SetPollingMode( | 127 | virtual Common::Input::DriverResult SetPollingMode( |
| 124 | [[maybe_unused]] const PadIdentifier& identifier, | 128 | [[maybe_unused]] const PadIdentifier& identifier, |
| 125 | [[maybe_unused]] const Common::Input::PollingMode polling_mode) { | 129 | [[maybe_unused]] const Common::Input::PollingMode polling_mode) { |
| 126 | return Common::Input::PollingError::NotSupported; | 130 | return Common::Input::DriverResult::NotSupported; |
| 127 | } | 131 | } |
| 128 | 132 | ||
| 129 | // Sets camera format to a controller | 133 | // Sets camera format to a controller |
| 130 | virtual Common::Input::CameraError SetCameraFormat( | 134 | virtual Common::Input::DriverResult SetCameraFormat( |
| 131 | [[maybe_unused]] const PadIdentifier& identifier, | 135 | [[maybe_unused]] const PadIdentifier& identifier, |
| 132 | [[maybe_unused]] Common::Input::CameraFormat camera_format) { | 136 | [[maybe_unused]] Common::Input::CameraFormat camera_format) { |
| 133 | return Common::Input::CameraError::NotSupported; | 137 | return Common::Input::DriverResult::NotSupported; |
| 134 | } | 138 | } |
| 135 | 139 | ||
| 136 | // Returns success if nfc is supported | 140 | // Returns success if nfc is supported |
| @@ -199,6 +203,7 @@ public: | |||
| 199 | bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; | 203 | bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; |
| 200 | f32 GetAxis(const PadIdentifier& identifier, int axis) const; | 204 | f32 GetAxis(const PadIdentifier& identifier, int axis) const; |
| 201 | Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; | 205 | Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; |
| 206 | Common::Input::BodyColorStatus GetColor(const PadIdentifier& identifier) const; | ||
| 202 | BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; | 207 | BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; |
| 203 | Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; | 208 | Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; |
| 204 | Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; | 209 | Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; |
| @@ -212,6 +217,7 @@ protected: | |||
| 212 | void SetHatButton(const PadIdentifier& identifier, int button, u8 value); | 217 | void SetHatButton(const PadIdentifier& identifier, int button, u8 value); |
| 213 | void SetAxis(const PadIdentifier& identifier, int axis, f32 value); | 218 | void SetAxis(const PadIdentifier& identifier, int axis, f32 value); |
| 214 | void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); | 219 | void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); |
| 220 | void SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value); | ||
| 215 | void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); | 221 | void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); |
| 216 | void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); | 222 | void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); |
| 217 | void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); | 223 | void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); |
| @@ -227,6 +233,7 @@ private: | |||
| 227 | std::unordered_map<int, float> axes; | 233 | std::unordered_map<int, float> axes; |
| 228 | std::unordered_map<int, BasicMotion> motions; | 234 | std::unordered_map<int, BasicMotion> motions; |
| 229 | Common::Input::BatteryLevel battery{}; | 235 | Common::Input::BatteryLevel battery{}; |
| 236 | Common::Input::BodyColorStatus color{}; | ||
| 230 | Common::Input::CameraStatus camera{}; | 237 | Common::Input::CameraStatus camera{}; |
| 231 | Common::Input::NfcStatus nfc{}; | 238 | Common::Input::NfcStatus nfc{}; |
| 232 | }; | 239 | }; |
| @@ -235,6 +242,8 @@ private: | |||
| 235 | void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); | 242 | void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); |
| 236 | void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); | 243 | void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); |
| 237 | void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); | 244 | void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); |
| 245 | void TriggerOnColorChange(const PadIdentifier& identifier, | ||
| 246 | Common::Input::BodyColorStatus value); | ||
| 238 | void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, | 247 | void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, |
| 239 | const BasicMotion& value); | 248 | const BasicMotion& value); |
| 240 | void TriggerOnCameraChange(const PadIdentifier& identifier, | 249 | void TriggerOnCameraChange(const PadIdentifier& identifier, |
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index fb8be42e2..15cbf7e5f 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp | |||
| @@ -498,6 +498,58 @@ private: | |||
| 498 | InputEngine* input_engine; | 498 | InputEngine* input_engine; |
| 499 | }; | 499 | }; |
| 500 | 500 | ||
| 501 | class InputFromColor final : public Common::Input::InputDevice { | ||
| 502 | public: | ||
| 503 | explicit InputFromColor(PadIdentifier identifier_, InputEngine* input_engine_) | ||
| 504 | : identifier(identifier_), input_engine(input_engine_) { | ||
| 505 | UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||
| 506 | const InputIdentifier input_identifier{ | ||
| 507 | .identifier = identifier, | ||
| 508 | .type = EngineInputType::Color, | ||
| 509 | .index = 0, | ||
| 510 | .callback = engine_callback, | ||
| 511 | }; | ||
| 512 | last_color_value = {}; | ||
| 513 | callback_key = input_engine->SetCallback(input_identifier); | ||
| 514 | } | ||
| 515 | |||
| 516 | ~InputFromColor() override { | ||
| 517 | input_engine->DeleteCallback(callback_key); | ||
| 518 | } | ||
| 519 | |||
| 520 | Common::Input::BodyColorStatus GetStatus() const { | ||
| 521 | return input_engine->GetColor(identifier); | ||
| 522 | } | ||
| 523 | |||
| 524 | void ForceUpdate() override { | ||
| 525 | const Common::Input::CallbackStatus status{ | ||
| 526 | .type = Common::Input::InputType::Color, | ||
| 527 | .color_status = GetStatus(), | ||
| 528 | }; | ||
| 529 | |||
| 530 | last_color_value = status.color_status; | ||
| 531 | TriggerOnChange(status); | ||
| 532 | } | ||
| 533 | |||
| 534 | void OnChange() { | ||
| 535 | const Common::Input::CallbackStatus status{ | ||
| 536 | .type = Common::Input::InputType::Color, | ||
| 537 | .color_status = GetStatus(), | ||
| 538 | }; | ||
| 539 | |||
| 540 | if (status.color_status.body != last_color_value.body) { | ||
| 541 | last_color_value = status.color_status; | ||
| 542 | TriggerOnChange(status); | ||
| 543 | } | ||
| 544 | } | ||
| 545 | |||
| 546 | private: | ||
| 547 | const PadIdentifier identifier; | ||
| 548 | int callback_key; | ||
| 549 | Common::Input::BodyColorStatus last_color_value; | ||
| 550 | InputEngine* input_engine; | ||
| 551 | }; | ||
| 552 | |||
| 501 | class InputFromMotion final : public Common::Input::InputDevice { | 553 | class InputFromMotion final : public Common::Input::InputDevice { |
| 502 | public: | 554 | public: |
| 503 | explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_, | 555 | explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_, |
| @@ -754,11 +806,11 @@ public: | |||
| 754 | explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) | 806 | explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) |
| 755 | : identifier(identifier_), input_engine(input_engine_) {} | 807 | : identifier(identifier_), input_engine(input_engine_) {} |
| 756 | 808 | ||
| 757 | void SetLED(const Common::Input::LedStatus& led_status) override { | 809 | Common::Input::DriverResult SetLED(const Common::Input::LedStatus& led_status) override { |
| 758 | input_engine->SetLeds(identifier, led_status); | 810 | return input_engine->SetLeds(identifier, led_status); |
| 759 | } | 811 | } |
| 760 | 812 | ||
| 761 | Common::Input::VibrationError SetVibration( | 813 | Common::Input::DriverResult SetVibration( |
| 762 | const Common::Input::VibrationStatus& vibration_status) override { | 814 | const Common::Input::VibrationStatus& vibration_status) override { |
| 763 | return input_engine->SetVibration(identifier, vibration_status); | 815 | return input_engine->SetVibration(identifier, vibration_status); |
| 764 | } | 816 | } |
| @@ -767,11 +819,12 @@ public: | |||
| 767 | return input_engine->IsVibrationEnabled(identifier); | 819 | return input_engine->IsVibrationEnabled(identifier); |
| 768 | } | 820 | } |
| 769 | 821 | ||
| 770 | Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { | 822 | Common::Input::DriverResult SetPollingMode(Common::Input::PollingMode polling_mode) override { |
| 771 | return input_engine->SetPollingMode(identifier, polling_mode); | 823 | return input_engine->SetPollingMode(identifier, polling_mode); |
| 772 | } | 824 | } |
| 773 | 825 | ||
| 774 | Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override { | 826 | Common::Input::DriverResult SetCameraFormat( |
| 827 | Common::Input::CameraFormat camera_format) override { | ||
| 775 | return input_engine->SetCameraFormat(identifier, camera_format); | 828 | return input_engine->SetCameraFormat(identifier, camera_format); |
| 776 | } | 829 | } |
| 777 | 830 | ||
| @@ -966,6 +1019,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice( | |||
| 966 | return std::make_unique<InputFromBattery>(identifier, input_engine.get()); | 1019 | return std::make_unique<InputFromBattery>(identifier, input_engine.get()); |
| 967 | } | 1020 | } |
| 968 | 1021 | ||
| 1022 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateColorDevice( | ||
| 1023 | const Common::ParamPackage& params) { | ||
| 1024 | const PadIdentifier identifier = { | ||
| 1025 | .guid = Common::UUID{params.Get("guid", "")}, | ||
| 1026 | .port = static_cast<std::size_t>(params.Get("port", 0)), | ||
| 1027 | .pad = static_cast<std::size_t>(params.Get("pad", 0)), | ||
| 1028 | }; | ||
| 1029 | |||
| 1030 | input_engine->PreSetController(identifier); | ||
| 1031 | return std::make_unique<InputFromColor>(identifier, input_engine.get()); | ||
| 1032 | } | ||
| 1033 | |||
| 969 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( | 1034 | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( |
| 970 | Common::ParamPackage params) { | 1035 | Common::ParamPackage params) { |
| 971 | const PadIdentifier identifier = { | 1036 | const PadIdentifier identifier = { |
| @@ -1053,6 +1118,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create( | |||
| 1053 | if (params.Has("battery")) { | 1118 | if (params.Has("battery")) { |
| 1054 | return CreateBatteryDevice(params); | 1119 | return CreateBatteryDevice(params); |
| 1055 | } | 1120 | } |
| 1121 | if (params.Has("color")) { | ||
| 1122 | return CreateColorDevice(params); | ||
| 1123 | } | ||
| 1056 | if (params.Has("camera")) { | 1124 | if (params.Has("camera")) { |
| 1057 | return CreateCameraDevice(params); | 1125 | return CreateCameraDevice(params); |
| 1058 | } | 1126 | } |
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h index d7db13ce4..e097e254c 100644 --- a/src/input_common/input_poller.h +++ b/src/input_common/input_poller.h | |||
| @@ -191,6 +191,17 @@ private: | |||
| 191 | const Common::ParamPackage& params); | 191 | const Common::ParamPackage& params); |
| 192 | 192 | ||
| 193 | /** | 193 | /** |
| 194 | * Creates a color device from the parameters given. | ||
| 195 | * @param params contains parameters for creating the device: | ||
| 196 | * - "guid": text string for identifying controllers | ||
| 197 | * - "port": port of the connected device | ||
| 198 | * - "pad": slot of the connected controller | ||
| 199 | * @returns a unique input device with the parameters specified | ||
| 200 | */ | ||
| 201 | std::unique_ptr<Common::Input::InputDevice> CreateColorDevice( | ||
| 202 | const Common::ParamPackage& params); | ||
| 203 | |||
| 204 | /** | ||
| 194 | * Creates a motion device from the parameters given. | 205 | * Creates a motion device from the parameters given. |
| 195 | * @param params contains parameters for creating the device: | 206 | * @param params contains parameters for creating the device: |
| 196 | * - "axis_x": the controller horizontal axis id to bind with the input | 207 | * - "axis_x": the controller horizontal axis id to bind with the input |
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index e0b2131ed..c77fc04ee 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include "input_common/drivers/gc_adapter.h" | 23 | #include "input_common/drivers/gc_adapter.h" |
| 24 | #endif | 24 | #endif |
| 25 | #ifdef HAVE_SDL2 | 25 | #ifdef HAVE_SDL2 |
| 26 | #include "input_common/drivers/joycon.h" | ||
| 26 | #include "input_common/drivers/sdl_driver.h" | 27 | #include "input_common/drivers/sdl_driver.h" |
| 27 | #endif | 28 | #endif |
| 28 | 29 | ||
| @@ -81,6 +82,7 @@ struct InputSubsystem::Impl { | |||
| 81 | RegisterEngine("virtual_gamepad", virtual_gamepad); | 82 | RegisterEngine("virtual_gamepad", virtual_gamepad); |
| 82 | #ifdef HAVE_SDL2 | 83 | #ifdef HAVE_SDL2 |
| 83 | RegisterEngine("sdl", sdl); | 84 | RegisterEngine("sdl", sdl); |
| 85 | RegisterEngine("joycon", joycon); | ||
| 84 | #endif | 86 | #endif |
| 85 | 87 | ||
| 86 | Common::Input::RegisterInputFactory("touch_from_button", | 88 | Common::Input::RegisterInputFactory("touch_from_button", |
| @@ -111,6 +113,7 @@ struct InputSubsystem::Impl { | |||
| 111 | UnregisterEngine(virtual_gamepad); | 113 | UnregisterEngine(virtual_gamepad); |
| 112 | #ifdef HAVE_SDL2 | 114 | #ifdef HAVE_SDL2 |
| 113 | UnregisterEngine(sdl); | 115 | UnregisterEngine(sdl); |
| 116 | UnregisterEngine(joycon); | ||
| 114 | #endif | 117 | #endif |
| 115 | 118 | ||
| 116 | Common::Input::UnregisterInputFactory("touch_from_button"); | 119 | Common::Input::UnregisterInputFactory("touch_from_button"); |
| @@ -133,6 +136,8 @@ struct InputSubsystem::Impl { | |||
| 133 | auto udp_devices = udp_client->GetInputDevices(); | 136 | auto udp_devices = udp_client->GetInputDevices(); |
| 134 | devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); | 137 | devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); |
| 135 | #ifdef HAVE_SDL2 | 138 | #ifdef HAVE_SDL2 |
| 139 | auto joycon_devices = joycon->GetInputDevices(); | ||
| 140 | devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end()); | ||
| 136 | auto sdl_devices = sdl->GetInputDevices(); | 141 | auto sdl_devices = sdl->GetInputDevices(); |
| 137 | devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); | 142 | devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); |
| 138 | #endif | 143 | #endif |
| @@ -164,6 +169,9 @@ struct InputSubsystem::Impl { | |||
| 164 | if (engine == sdl->GetEngineName()) { | 169 | if (engine == sdl->GetEngineName()) { |
| 165 | return sdl; | 170 | return sdl; |
| 166 | } | 171 | } |
| 172 | if (engine == joycon->GetEngineName()) { | ||
| 173 | return joycon; | ||
| 174 | } | ||
| 167 | #endif | 175 | #endif |
| 168 | return nullptr; | 176 | return nullptr; |
| 169 | } | 177 | } |
| @@ -247,6 +255,9 @@ struct InputSubsystem::Impl { | |||
| 247 | if (engine == sdl->GetEngineName()) { | 255 | if (engine == sdl->GetEngineName()) { |
| 248 | return true; | 256 | return true; |
| 249 | } | 257 | } |
| 258 | if (engine == joycon->GetEngineName()) { | ||
| 259 | return true; | ||
| 260 | } | ||
| 250 | #endif | 261 | #endif |
| 251 | return false; | 262 | return false; |
| 252 | } | 263 | } |
| @@ -260,6 +271,7 @@ struct InputSubsystem::Impl { | |||
| 260 | udp_client->BeginConfiguration(); | 271 | udp_client->BeginConfiguration(); |
| 261 | #ifdef HAVE_SDL2 | 272 | #ifdef HAVE_SDL2 |
| 262 | sdl->BeginConfiguration(); | 273 | sdl->BeginConfiguration(); |
| 274 | joycon->BeginConfiguration(); | ||
| 263 | #endif | 275 | #endif |
| 264 | } | 276 | } |
| 265 | 277 | ||
| @@ -272,6 +284,7 @@ struct InputSubsystem::Impl { | |||
| 272 | udp_client->EndConfiguration(); | 284 | udp_client->EndConfiguration(); |
| 273 | #ifdef HAVE_SDL2 | 285 | #ifdef HAVE_SDL2 |
| 274 | sdl->EndConfiguration(); | 286 | sdl->EndConfiguration(); |
| 287 | joycon->EndConfiguration(); | ||
| 275 | #endif | 288 | #endif |
| 276 | } | 289 | } |
| 277 | 290 | ||
| @@ -304,6 +317,7 @@ struct InputSubsystem::Impl { | |||
| 304 | 317 | ||
| 305 | #ifdef HAVE_SDL2 | 318 | #ifdef HAVE_SDL2 |
| 306 | std::shared_ptr<SDLDriver> sdl; | 319 | std::shared_ptr<SDLDriver> sdl; |
| 320 | std::shared_ptr<Joycons> joycon; | ||
| 307 | #endif | 321 | #endif |
| 308 | }; | 322 | }; |
| 309 | 323 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index fb5799c42..c898ce12f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -436,6 +436,10 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c | |||
| 436 | if (info.type == TextureType::Buffer) { | 436 | if (info.type == TextureType::Buffer) { |
| 437 | lod = Id{}; | 437 | lod = Id{}; |
| 438 | } | 438 | } |
| 439 | if (Sirit::ValidId(ms)) { | ||
| 440 | // This image is multisampled, lod must be implicit | ||
| 441 | lod = Id{}; | ||
| 442 | } | ||
| 439 | const ImageOperands operands(offset, lod, ms); | 443 | const ImageOperands operands(offset, lod, ms); |
| 440 | return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], | 444 | return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], |
| 441 | TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); | 445 | TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index a0c155fdb..3b97721e1 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -35,6 +35,7 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { | |||
| 35 | const spv::ImageFormat format{spv::ImageFormat::Unknown}; | 35 | const spv::ImageFormat format{spv::ImageFormat::Unknown}; |
| 36 | const Id type{ctx.F32[1]}; | 36 | const Id type{ctx.F32[1]}; |
| 37 | const bool depth{desc.is_depth}; | 37 | const bool depth{desc.is_depth}; |
| 38 | const bool ms{desc.is_multisample}; | ||
| 38 | switch (desc.type) { | 39 | switch (desc.type) { |
| 39 | case TextureType::Color1D: | 40 | case TextureType::Color1D: |
| 40 | return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); | 41 | return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); |
| @@ -42,9 +43,9 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { | |||
| 42 | return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); | 43 | return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); |
| 43 | case TextureType::Color2D: | 44 | case TextureType::Color2D: |
| 44 | case TextureType::Color2DRect: | 45 | case TextureType::Color2DRect: |
| 45 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); | 46 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format); |
| 46 | case TextureType::ColorArray2D: | 47 | case TextureType::ColorArray2D: |
| 47 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); | 48 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format); |
| 48 | case TextureType::Color3D: | 49 | case TextureType::Color3D: |
| 49 | return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); | 50 | return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); |
| 50 | case TextureType::ColorCube: | 51 | case TextureType::ColorCube: |
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index f5c86fcb1..9718c6921 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp | |||
| @@ -524,6 +524,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo | |||
| 524 | 524 | ||
| 525 | const auto& cbuf{texture_inst.cbuf}; | 525 | const auto& cbuf{texture_inst.cbuf}; |
| 526 | auto flags{inst->Flags<IR::TextureInstInfo>()}; | 526 | auto flags{inst->Flags<IR::TextureInstInfo>()}; |
| 527 | bool is_multisample{false}; | ||
| 527 | switch (inst->GetOpcode()) { | 528 | switch (inst->GetOpcode()) { |
| 528 | case IR::Opcode::ImageQueryDimensions: | 529 | case IR::Opcode::ImageQueryDimensions: |
| 529 | flags.type.Assign(ReadTextureType(env, cbuf)); | 530 | flags.type.Assign(ReadTextureType(env, cbuf)); |
| @@ -538,6 +539,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo | |||
| 538 | } | 539 | } |
| 539 | break; | 540 | break; |
| 540 | case IR::Opcode::ImageFetch: | 541 | case IR::Opcode::ImageFetch: |
| 542 | if (flags.type == TextureType::Color2D || flags.type == TextureType::Color2DRect || | ||
| 543 | flags.type == TextureType::ColorArray2D) { | ||
| 544 | is_multisample = !inst->Arg(4).IsEmpty(); | ||
| 545 | } else { | ||
| 546 | inst->SetArg(4, IR::U32{}); | ||
| 547 | } | ||
| 541 | if (flags.type != TextureType::Color1D) { | 548 | if (flags.type != TextureType::Color1D) { |
| 542 | break; | 549 | break; |
| 543 | } | 550 | } |
| @@ -613,6 +620,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo | |||
| 613 | index = descriptors.Add(TextureDescriptor{ | 620 | index = descriptors.Add(TextureDescriptor{ |
| 614 | .type = flags.type, | 621 | .type = flags.type, |
| 615 | .is_depth = flags.is_depth != 0, | 622 | .is_depth = flags.is_depth != 0, |
| 623 | .is_multisample = is_multisample, | ||
| 616 | .has_secondary = cbuf.has_secondary, | 624 | .has_secondary = cbuf.has_secondary, |
| 617 | .cbuf_index = cbuf.index, | 625 | .cbuf_index = cbuf.index, |
| 618 | .cbuf_offset = cbuf.offset, | 626 | .cbuf_offset = cbuf.offset, |
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index f93181e1e..d308db942 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h | |||
| @@ -109,6 +109,7 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip | |||
| 109 | struct TextureDescriptor { | 109 | struct TextureDescriptor { |
| 110 | TextureType type; | 110 | TextureType type; |
| 111 | bool is_depth; | 111 | bool is_depth; |
| 112 | bool is_multisample; | ||
| 112 | bool has_secondary; | 113 | bool has_secondary; |
| 113 | u32 cbuf_index; | 114 | u32 cbuf_index; |
| 114 | u32 cbuf_offset; | 115 | u32 cbuf_offset; |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 9b65e79cb..ae84408bc 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -14,7 +14,6 @@ add_executable(tests | |||
| 14 | core/core_timing.cpp | 14 | core/core_timing.cpp |
| 15 | core/internal_network/network.cpp | 15 | core/internal_network/network.cpp |
| 16 | precompiled_headers.h | 16 | precompiled_headers.h |
| 17 | tests.cpp | ||
| 18 | video_core/buffer_base.cpp | 17 | video_core/buffer_base.cpp |
| 19 | input_common/calibration_configuration_job.cpp | 18 | input_common/calibration_configuration_job.cpp |
| 20 | ) | 19 | ) |
| @@ -22,7 +21,7 @@ add_executable(tests | |||
| 22 | create_target_directory_groups(tests) | 21 | create_target_directory_groups(tests) |
| 23 | 22 | ||
| 24 | target_link_libraries(tests PRIVATE common core input_common) | 23 | target_link_libraries(tests PRIVATE common core input_common) |
| 25 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads) | 24 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain Threads::Threads) |
| 26 | 25 | ||
| 27 | add_test(NAME tests COMMAND tests) | 26 | add_test(NAME tests COMMAND tests) |
| 28 | 27 | ||
diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp index 0071ae52e..75e990ecd 100644 --- a/src/tests/common/bit_field.cpp +++ b/src/tests/common/bit_field.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include <array> | 4 | #include <array> |
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <type_traits> | 6 | #include <type_traits> |
| 7 | #include <catch2/catch.hpp> | 7 | #include <catch2/catch_test_macros.hpp> |
| 8 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 9 | 9 | ||
| 10 | TEST_CASE("BitField", "[common]") { | 10 | TEST_CASE("BitField", "[common]") { |
diff --git a/src/tests/common/cityhash.cpp b/src/tests/common/cityhash.cpp index 05942eadb..2a391dff1 100644 --- a/src/tests/common/cityhash.cpp +++ b/src/tests/common/cityhash.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <catch2/catch.hpp> | 4 | #include <catch2/catch_test_macros.hpp> |
| 5 | 5 | ||
| 6 | #include "common/cityhash.h" | 6 | #include "common/cityhash.h" |
| 7 | 7 | ||
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index 4e29f9199..ecad7583f 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include <unordered_map> | 11 | #include <unordered_map> |
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | 13 | ||
| 14 | #include <catch2/catch.hpp> | 14 | #include <catch2/catch_test_macros.hpp> |
| 15 | 15 | ||
| 16 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 17 | #include "common/fiber.h" | 17 | #include "common/fiber.h" |
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp index e49d0a09f..1b014b632 100644 --- a/src/tests/common/host_memory.cpp +++ b/src/tests/common/host_memory.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <catch2/catch.hpp> | 4 | #include <catch2/catch_test_macros.hpp> |
| 5 | 5 | ||
| 6 | #include "common/host_memory.h" | 6 | #include "common/host_memory.h" |
| 7 | #include "common/literals.h" | 7 | #include "common/literals.h" |
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp index d036cc83a..41575def4 100644 --- a/src/tests/common/param_package.cpp +++ b/src/tests/common/param_package.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <catch2/catch.hpp> | 4 | #include <catch2/catch_test_macros.hpp> |
| 5 | #include <math.h> | 5 | #include <math.h> |
| 6 | #include "common/logging/backend.h" | 6 | #include "common/logging/backend.h" |
| 7 | #include "common/param_package.h" | 7 | #include "common/param_package.h" |
diff --git a/src/tests/common/range_map.cpp b/src/tests/common/range_map.cpp index 5a4630a38..d301ac5f6 100644 --- a/src/tests/common/range_map.cpp +++ b/src/tests/common/range_map.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <stdexcept> | 4 | #include <stdexcept> |
| 5 | 5 | ||
| 6 | #include <catch2/catch.hpp> | 6 | #include <catch2/catch_test_macros.hpp> |
| 7 | 7 | ||
| 8 | #include "common/range_map.h" | 8 | #include "common/range_map.h" |
| 9 | 9 | ||
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp index 4f81b6e5e..7dee988c8 100644 --- a/src/tests/common/ring_buffer.cpp +++ b/src/tests/common/ring_buffer.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <numeric> | 7 | #include <numeric> |
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include <catch2/catch.hpp> | 10 | #include <catch2/catch_test_macros.hpp> |
| 11 | #include "common/ring_buffer.h" | 11 | #include "common/ring_buffer.h" |
| 12 | 12 | ||
| 13 | namespace Common { | 13 | namespace Common { |
diff --git a/src/tests/common/scratch_buffer.cpp b/src/tests/common/scratch_buffer.cpp index f6e50da4a..132f139fa 100644 --- a/src/tests/common/scratch_buffer.cpp +++ b/src/tests/common/scratch_buffer.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <span> | 7 | #include <span> |
| 8 | #include <catch2/catch.hpp> | 8 | #include <catch2/catch_test_macros.hpp> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/scratch_buffer.h" | 10 | #include "common/scratch_buffer.h" |
| 11 | 11 | ||
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp index 311272506..f7a23e876 100644 --- a/src/tests/common/unique_function.cpp +++ b/src/tests/common/unique_function.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <string> | 4 | #include <string> |
| 5 | 5 | ||
| 6 | #include <catch2/catch.hpp> | 6 | #include <catch2/catch_test_macros.hpp> |
| 7 | 7 | ||
| 8 | #include "common/unique_function.h" | 8 | #include "common/unique_function.h" |
| 9 | 9 | ||
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 284b2ae66..f08afbf9a 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2016 Dolphin Emulator Project | 1 | // SPDX-FileCopyrightText: 2016 Dolphin Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <catch2/catch.hpp> | 4 | #include <catch2/catch_test_macros.hpp> |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <bitset> | 7 | #include <bitset> |
diff --git a/src/tests/core/internal_network/network.cpp b/src/tests/core/internal_network/network.cpp index 164b0ff24..10ddd8b42 100644 --- a/src/tests/core/internal_network/network.cpp +++ b/src/tests/core/internal_network/network.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <catch2/catch.hpp> | 4 | #include <catch2/catch_test_macros.hpp> |
| 5 | 5 | ||
| 6 | #include "core/internal_network/network.h" | 6 | #include "core/internal_network/network.h" |
| 7 | #include "core/internal_network/sockets.h" | 7 | #include "core/internal_network/sockets.h" |
diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp index e5f698886..516ff1b30 100644 --- a/src/tests/input_common/calibration_configuration_job.cpp +++ b/src/tests/input_common/calibration_configuration_job.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include <thread> | 6 | #include <thread> |
| 7 | #include <boost/asio.hpp> | 7 | #include <boost/asio.hpp> |
| 8 | #include <boost/crc.hpp> | 8 | #include <boost/crc.hpp> |
| 9 | #include <catch2/catch.hpp> | 9 | #include <catch2/catch_test_macros.hpp> |
| 10 | 10 | ||
| 11 | #include "input_common/drivers/udp_client.h" | 11 | #include "input_common/drivers/udp_client.h" |
| 12 | #include "input_common/helpers/udp_protocol.h" | 12 | #include "input_common/helpers/udp_protocol.h" |
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp deleted file mode 100644 index 3f905c05c..000000000 --- a/src/tests/tests.cpp +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #define CATCH_CONFIG_MAIN | ||
| 5 | #include <catch2/catch.hpp> | ||
| 6 | |||
| 7 | // Catch provides the main function since we've given it the | ||
| 8 | // CATCH_CONFIG_MAIN preprocessor directive. | ||
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index 5cd0628f2..1275cca24 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include <stdexcept> | 4 | #include <stdexcept> |
| 5 | #include <unordered_map> | 5 | #include <unordered_map> |
| 6 | 6 | ||
| 7 | #include <catch2/catch.hpp> | 7 | #include <catch2/catch_test_macros.hpp> |
| 8 | 8 | ||
| 9 | #include "common/alignment.h" | 9 | #include "common/alignment.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 3bcae3503..83924475b 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include "common/alignment.h" | 6 | #include "common/alignment.h" |
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/settings.h" | ||
| 10 | #include "core/core.h" | 9 | #include "core/core.h" |
| 11 | #include "core/device_memory.h" | 10 | #include "core/device_memory.h" |
| 12 | #include "core/hle/kernel/k_page_table.h" | 11 | #include "core/hle/kernel/k_page_table.h" |
| @@ -46,11 +45,6 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 | |||
| 46 | big_page_table_cpu.resize(big_page_table_size); | 45 | big_page_table_cpu.resize(big_page_table_size); |
| 47 | big_page_continous.resize(big_page_table_size / continous_bits, 0); | 46 | big_page_continous.resize(big_page_table_size / continous_bits, 0); |
| 48 | entries.resize(page_table_size / 32, 0); | 47 | entries.resize(page_table_size / 32, 0); |
| 49 | if (!Settings::IsGPULevelExtreme() && Settings::IsFastmemEnabled()) { | ||
| 50 | fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); | ||
| 51 | } else { | ||
| 52 | fastmem_arena = nullptr; | ||
| 53 | } | ||
| 54 | } | 48 | } |
| 55 | 49 | ||
| 56 | MemoryManager::~MemoryManager() = default; | 50 | MemoryManager::~MemoryManager() = default; |
| @@ -360,7 +354,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si | |||
| 360 | } | 354 | } |
| 361 | } | 355 | } |
| 362 | 356 | ||
| 363 | template <bool is_safe, bool use_fastmem> | 357 | template <bool is_safe> |
| 364 | void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, | 358 | void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, |
| 365 | [[maybe_unused]] VideoCommon::CacheType which) const { | 359 | [[maybe_unused]] VideoCommon::CacheType which) const { |
| 366 | auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, | 360 | auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, |
| @@ -374,12 +368,8 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std: | |||
| 374 | if constexpr (is_safe) { | 368 | if constexpr (is_safe) { |
| 375 | rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); | 369 | rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); |
| 376 | } | 370 | } |
| 377 | if constexpr (use_fastmem) { | 371 | u8* physical = memory.GetPointer(cpu_addr_base); |
| 378 | std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); | 372 | std::memcpy(dest_buffer, physical, copy_amount); |
| 379 | } else { | ||
| 380 | u8* physical = memory.GetPointer(cpu_addr_base); | ||
| 381 | std::memcpy(dest_buffer, physical, copy_amount); | ||
| 382 | } | ||
| 383 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | 373 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; |
| 384 | }; | 374 | }; |
| 385 | auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { | 375 | auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { |
| @@ -388,15 +378,11 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std: | |||
| 388 | if constexpr (is_safe) { | 378 | if constexpr (is_safe) { |
| 389 | rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); | 379 | rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); |
| 390 | } | 380 | } |
| 391 | if constexpr (use_fastmem) { | 381 | if (!IsBigPageContinous(page_index)) [[unlikely]] { |
| 392 | std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); | 382 | memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); |
| 393 | } else { | 383 | } else { |
| 394 | if (!IsBigPageContinous(page_index)) [[unlikely]] { | 384 | u8* physical = memory.GetPointer(cpu_addr_base); |
| 395 | memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); | 385 | std::memcpy(dest_buffer, physical, copy_amount); |
| 396 | } else { | ||
| 397 | u8* physical = memory.GetPointer(cpu_addr_base); | ||
| 398 | std::memcpy(dest_buffer, physical, copy_amount); | ||
| 399 | } | ||
| 400 | } | 386 | } |
| 401 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | 387 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; |
| 402 | }; | 388 | }; |
| @@ -410,20 +396,12 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std: | |||
| 410 | 396 | ||
| 411 | void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, | 397 | void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, |
| 412 | VideoCommon::CacheType which) const { | 398 | VideoCommon::CacheType which) const { |
| 413 | if (fastmem_arena) [[likely]] { | 399 | ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which); |
| 414 | ReadBlockImpl<true, true>(gpu_src_addr, dest_buffer, size, which); | ||
| 415 | return; | ||
| 416 | } | ||
| 417 | ReadBlockImpl<true, false>(gpu_src_addr, dest_buffer, size, which); | ||
| 418 | } | 400 | } |
| 419 | 401 | ||
| 420 | void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, | 402 | void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, |
| 421 | const std::size_t size) const { | 403 | const std::size_t size) const { |
| 422 | if (fastmem_arena) [[likely]] { | 404 | ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None); |
| 423 | ReadBlockImpl<false, true>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None); | ||
| 424 | return; | ||
| 425 | } | ||
| 426 | ReadBlockImpl<false, false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None); | ||
| 427 | } | 405 | } |
| 428 | 406 | ||
| 429 | template <bool is_safe> | 407 | template <bool is_safe> |
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 2936364f0..9ebfb6179 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -141,7 +141,7 @@ private: | |||
| 141 | inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, | 141 | inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, |
| 142 | FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; | 142 | FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; |
| 143 | 143 | ||
| 144 | template <bool is_safe, bool use_fastmem> | 144 | template <bool is_safe> |
| 145 | void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, | 145 | void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, |
| 146 | VideoCommon::CacheType which) const; | 146 | VideoCommon::CacheType which) const; |
| 147 | 147 | ||
| @@ -215,7 +215,6 @@ private: | |||
| 215 | 215 | ||
| 216 | std::vector<u64> big_page_continous; | 216 | std::vector<u64> big_page_continous; |
| 217 | std::vector<std::pair<VAddr, std::size_t>> page_stash{}; | 217 | std::vector<std::pair<VAddr, std::size_t>> page_stash{}; |
| 218 | u8* fastmem_arena{}; | ||
| 219 | 218 | ||
| 220 | constexpr static size_t continous_bits = 64; | 219 | constexpr static size_t continous_bits = 64; |
| 221 | 220 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index fd3bb30e1..35fef506a 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -440,6 +440,7 @@ void Config::ReadControlValues() { | |||
| 440 | ReadBasicSetting(Settings::values.emulate_analog_keyboard); | 440 | ReadBasicSetting(Settings::values.emulate_analog_keyboard); |
| 441 | Settings::values.mouse_panning = false; | 441 | Settings::values.mouse_panning = false; |
| 442 | ReadBasicSetting(Settings::values.mouse_panning_sensitivity); | 442 | ReadBasicSetting(Settings::values.mouse_panning_sensitivity); |
| 443 | ReadBasicSetting(Settings::values.enable_joycon_driver); | ||
| 443 | 444 | ||
| 444 | ReadBasicSetting(Settings::values.tas_enable); | 445 | ReadBasicSetting(Settings::values.tas_enable); |
| 445 | ReadBasicSetting(Settings::values.tas_loop); | 446 | ReadBasicSetting(Settings::values.tas_loop); |
| @@ -1139,6 +1140,7 @@ void Config::SaveControlValues() { | |||
| 1139 | WriteGlobalSetting(Settings::values.enable_accurate_vibrations); | 1140 | WriteGlobalSetting(Settings::values.enable_accurate_vibrations); |
| 1140 | WriteGlobalSetting(Settings::values.motion_enabled); | 1141 | WriteGlobalSetting(Settings::values.motion_enabled); |
| 1141 | WriteBasicSetting(Settings::values.enable_raw_input); | 1142 | WriteBasicSetting(Settings::values.enable_raw_input); |
| 1143 | WriteBasicSetting(Settings::values.enable_joycon_driver); | ||
| 1142 | WriteBasicSetting(Settings::values.keyboard_enabled); | 1144 | WriteBasicSetting(Settings::values.keyboard_enabled); |
| 1143 | WriteBasicSetting(Settings::values.emulate_analog_keyboard); | 1145 | WriteBasicSetting(Settings::values.emulate_analog_keyboard); |
| 1144 | WriteBasicSetting(Settings::values.mouse_panning_sensitivity); | 1146 | WriteBasicSetting(Settings::values.mouse_panning_sensitivity); |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 4301313cf..2aaefcc05 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -66,7 +66,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, | |||
| 66 | 66 | ||
| 67 | web_tab->SetWebServiceConfigEnabled(enable_web_config); | 67 | web_tab->SetWebServiceConfigEnabled(enable_web_config); |
| 68 | hotkeys_tab->Populate(registry); | 68 | hotkeys_tab->Populate(registry); |
| 69 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 70 | 69 | ||
| 71 | input_tab->Initialize(input_subsystem); | 70 | input_tab->Initialize(input_subsystem); |
| 72 | 71 | ||
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 235b813d9..77b976e74 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp | |||
| @@ -138,6 +138,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() { | |||
| 138 | Settings::values.controller_navigation = ui->controller_navigation->isChecked(); | 138 | Settings::values.controller_navigation = ui->controller_navigation->isChecked(); |
| 139 | Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); | 139 | Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); |
| 140 | Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); | 140 | Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); |
| 141 | Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked(); | ||
| 141 | } | 142 | } |
| 142 | 143 | ||
| 143 | void ConfigureInputAdvanced::LoadConfiguration() { | 144 | void ConfigureInputAdvanced::LoadConfiguration() { |
| @@ -172,6 +173,7 @@ void ConfigureInputAdvanced::LoadConfiguration() { | |||
| 172 | ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); | 173 | ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); |
| 173 | ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); | 174 | ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); |
| 174 | ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); | 175 | ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); |
| 176 | ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue()); | ||
| 175 | 177 | ||
| 176 | UpdateUIEnabled(); | 178 | UpdateUIEnabled(); |
| 177 | } | 179 | } |
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index fac8cf827..75d96d3ab 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui | |||
| @@ -2696,6 +2696,22 @@ | |||
| 2696 | </widget> | 2696 | </widget> |
| 2697 | </item> | 2697 | </item> |
| 2698 | <item row="5" column="0"> | 2698 | <item row="5" column="0"> |
| 2699 | <widget class="QCheckBox" name="enable_joycon_driver"> | ||
| 2700 | <property name="toolTip"> | ||
| 2701 | <string>Requires restarting yuzu</string> | ||
| 2702 | </property> | ||
| 2703 | <property name="minimumSize"> | ||
| 2704 | <size> | ||
| 2705 | <width>0</width> | ||
| 2706 | <height>23</height> | ||
| 2707 | </size> | ||
| 2708 | </property> | ||
| 2709 | <property name="text"> | ||
| 2710 | <string>Enable direct JoyCon driver</string> | ||
| 2711 | </property> | ||
| 2712 | </widget> | ||
| 2713 | </item> | ||
| 2714 | <item row="6" column="0"> | ||
| 2699 | <widget class="QCheckBox" name="mouse_panning"> | 2715 | <widget class="QCheckBox" name="mouse_panning"> |
| 2700 | <property name="minimumSize"> | 2716 | <property name="minimumSize"> |
| 2701 | <size> | 2717 | <size> |
| @@ -2708,7 +2724,7 @@ | |||
| 2708 | </property> | 2724 | </property> |
| 2709 | </widget> | 2725 | </widget> |
| 2710 | </item> | 2726 | </item> |
| 2711 | <item row="5" column="2"> | 2727 | <item row="6" column="2"> |
| 2712 | <widget class="QSpinBox" name="mouse_panning_sensitivity"> | 2728 | <widget class="QSpinBox" name="mouse_panning_sensitivity"> |
| 2713 | <property name="toolTip"> | 2729 | <property name="toolTip"> |
| 2714 | <string>Mouse sensitivity</string> | 2730 | <string>Mouse sensitivity</string> |
| @@ -2730,14 +2746,14 @@ | |||
| 2730 | </property> | 2746 | </property> |
| 2731 | </widget> | 2747 | </widget> |
| 2732 | </item> | 2748 | </item> |
| 2733 | <item row="6" column="0"> | 2749 | <item row="7" column="0"> |
| 2734 | <widget class="QLabel" name="motion_touch"> | 2750 | <widget class="QLabel" name="motion_touch"> |
| 2735 | <property name="text"> | 2751 | <property name="text"> |
| 2736 | <string>Motion / Touch</string> | 2752 | <string>Motion / Touch</string> |
| 2737 | </property> | 2753 | </property> |
| 2738 | </widget> | 2754 | </widget> |
| 2739 | </item> | 2755 | </item> |
| 2740 | <item row="6" column="2"> | 2756 | <item row="7" column="2"> |
| 2741 | <widget class="QPushButton" name="buttonMotionTouch"> | 2757 | <widget class="QPushButton" name="buttonMotionTouch"> |
| 2742 | <property name="text"> | 2758 | <property name="text"> |
| 2743 | <string>Configure</string> | 2759 | <string>Configure</string> |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index c40d980c9..4b7e3b01b 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -66,6 +66,18 @@ QString GetButtonName(Common::Input::ButtonNames button_name) { | |||
| 66 | return QObject::tr("R"); | 66 | return QObject::tr("R"); |
| 67 | case Common::Input::ButtonNames::TriggerL: | 67 | case Common::Input::ButtonNames::TriggerL: |
| 68 | return QObject::tr("L"); | 68 | return QObject::tr("L"); |
| 69 | case Common::Input::ButtonNames::TriggerZR: | ||
| 70 | return QObject::tr("ZR"); | ||
| 71 | case Common::Input::ButtonNames::TriggerZL: | ||
| 72 | return QObject::tr("ZL"); | ||
| 73 | case Common::Input::ButtonNames::TriggerSR: | ||
| 74 | return QObject::tr("SR"); | ||
| 75 | case Common::Input::ButtonNames::TriggerSL: | ||
| 76 | return QObject::tr("SL"); | ||
| 77 | case Common::Input::ButtonNames::ButtonStickL: | ||
| 78 | return QObject::tr("Stick L"); | ||
| 79 | case Common::Input::ButtonNames::ButtonStickR: | ||
| 80 | return QObject::tr("Stick R"); | ||
| 69 | case Common::Input::ButtonNames::ButtonA: | 81 | case Common::Input::ButtonNames::ButtonA: |
| 70 | return QObject::tr("A"); | 82 | return QObject::tr("A"); |
| 71 | case Common::Input::ButtonNames::ButtonB: | 83 | case Common::Input::ButtonNames::ButtonB: |
| @@ -76,6 +88,14 @@ QString GetButtonName(Common::Input::ButtonNames button_name) { | |||
| 76 | return QObject::tr("Y"); | 88 | return QObject::tr("Y"); |
| 77 | case Common::Input::ButtonNames::ButtonStart: | 89 | case Common::Input::ButtonNames::ButtonStart: |
| 78 | return QObject::tr("Start"); | 90 | return QObject::tr("Start"); |
| 91 | case Common::Input::ButtonNames::ButtonPlus: | ||
| 92 | return QObject::tr("Plus"); | ||
| 93 | case Common::Input::ButtonNames::ButtonMinus: | ||
| 94 | return QObject::tr("Minus"); | ||
| 95 | case Common::Input::ButtonNames::ButtonHome: | ||
| 96 | return QObject::tr("Home"); | ||
| 97 | case Common::Input::ButtonNames::ButtonCapture: | ||
| 98 | return QObject::tr("Capture"); | ||
| 79 | case Common::Input::ButtonNames::L1: | 99 | case Common::Input::ButtonNames::L1: |
| 80 | return QObject::tr("L1"); | 100 | return QObject::tr("L1"); |
| 81 | case Common::Input::ButtonNames::L2: | 101 | case Common::Input::ButtonNames::L2: |
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index 11390fec0..68af6c20c 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp | |||
| @@ -103,9 +103,13 @@ void PlayerControlPreview::UpdateColors() { | |||
| 103 | 103 | ||
| 104 | colors.left = colors.primary; | 104 | colors.left = colors.primary; |
| 105 | colors.right = colors.primary; | 105 | colors.right = colors.primary; |
| 106 | // Possible alternative to set colors from settings | 106 | |
| 107 | // colors.left = QColor(controller->GetColors().left.body); | 107 | const auto color_left = controller->GetColorsValues()[0].body; |
| 108 | // colors.right = QColor(controller->GetColors().right.body); | 108 | const auto color_right = controller->GetColorsValues()[1].body; |
| 109 | if (color_left != 0 && color_right != 0) { | ||
| 110 | colors.left = QColor(color_left); | ||
| 111 | colors.right = QColor(color_right); | ||
| 112 | } | ||
| 109 | } | 113 | } |
| 110 | 114 | ||
| 111 | void PlayerControlPreview::ResetInputs() { | 115 | void PlayerControlPreview::ResetInputs() { |
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index d1b870c72..fb1292f07 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp | |||
| @@ -89,7 +89,6 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent, | |||
| 89 | "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " | 89 | "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " |
| 90 | "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); | 90 | "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); |
| 91 | 91 | ||
| 92 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 93 | SetConfiguration(); | 92 | SetConfiguration(); |
| 94 | UpdateUiDisplay(); | 93 | UpdateUiDisplay(); |
| 95 | ConnectEvents(); | 94 | ConnectEvents(); |
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 93db47cfd..7e757eafd 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp | |||
| @@ -66,8 +66,6 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st | |||
| 66 | 66 | ||
| 67 | setFocusPolicy(Qt::ClickFocus); | 67 | setFocusPolicy(Qt::ClickFocus); |
| 68 | setWindowTitle(tr("Properties")); | 68 | setWindowTitle(tr("Properties")); |
| 69 | // remove Help question mark button from the title bar | ||
| 70 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 71 | 69 | ||
| 72 | addons_tab->SetTitleId(title_id); | 70 | addons_tab->SetTitleId(title_id); |
| 73 | 71 | ||
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp index 688c2dd38..1275f10c8 100644 --- a/src/yuzu/configuration/configure_ringcon.cpp +++ b/src/yuzu/configuration/configure_ringcon.cpp | |||
| @@ -4,9 +4,11 @@ | |||
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | #include <QKeyEvent> | 5 | #include <QKeyEvent> |
| 6 | #include <QMenu> | 6 | #include <QMenu> |
| 7 | #include <QMessageBox> | ||
| 7 | #include <QTimer> | 8 | #include <QTimer> |
| 9 | #include <fmt/format.h> | ||
| 8 | 10 | ||
| 9 | #include "core/hid/emulated_devices.h" | 11 | #include "core/hid/emulated_controller.h" |
| 10 | #include "core/hid/hid_core.h" | 12 | #include "core/hid/hid_core.h" |
| 11 | #include "input_common/drivers/keyboard.h" | 13 | #include "input_common/drivers/keyboard.h" |
| 12 | #include "input_common/drivers/mouse.h" | 14 | #include "input_common/drivers/mouse.h" |
| @@ -126,9 +128,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent, | |||
| 126 | ui->buttonRingAnalogPush, | 128 | ui->buttonRingAnalogPush, |
| 127 | }; | 129 | }; |
| 128 | 130 | ||
| 129 | emulated_device = hid_core_.GetEmulatedDevices(); | 131 | emulated_controller = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 130 | emulated_device->SaveCurrentConfig(); | 132 | emulated_controller->SaveCurrentConfig(); |
| 131 | emulated_device->EnableConfiguration(); | 133 | emulated_controller->EnableConfiguration(); |
| 134 | |||
| 135 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 136 | .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); }, | ||
| 137 | .is_npad_service = false, | ||
| 138 | }; | ||
| 139 | callback_key = emulated_controller->SetCallback(engine_callback); | ||
| 140 | is_controller_set = true; | ||
| 132 | 141 | ||
| 133 | LoadConfiguration(); | 142 | LoadConfiguration(); |
| 134 | 143 | ||
| @@ -143,9 +152,9 @@ ConfigureRingController::ConfigureRingController(QWidget* parent, | |||
| 143 | HandleClick( | 152 | HandleClick( |
| 144 | analog_map_buttons[sub_button_id], | 153 | analog_map_buttons[sub_button_id], |
| 145 | [=, this](const Common::ParamPackage& params) { | 154 | [=, this](const Common::ParamPackage& params) { |
| 146 | Common::ParamPackage param = emulated_device->GetRingParam(); | 155 | Common::ParamPackage param = emulated_controller->GetRingParam(); |
| 147 | SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]); | 156 | SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]); |
| 148 | emulated_device->SetRingParam(param); | 157 | emulated_controller->SetRingParam(param); |
| 149 | }, | 158 | }, |
| 150 | InputCommon::Polling::InputType::Stick); | 159 | InputCommon::Polling::InputType::Stick); |
| 151 | }); | 160 | }); |
| @@ -155,16 +164,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent, | |||
| 155 | connect(analog_button, &QPushButton::customContextMenuRequested, | 164 | connect(analog_button, &QPushButton::customContextMenuRequested, |
| 156 | [=, this](const QPoint& menu_location) { | 165 | [=, this](const QPoint& menu_location) { |
| 157 | QMenu context_menu; | 166 | QMenu context_menu; |
| 158 | Common::ParamPackage param = emulated_device->GetRingParam(); | 167 | Common::ParamPackage param = emulated_controller->GetRingParam(); |
| 159 | context_menu.addAction(tr("Clear"), [&] { | 168 | context_menu.addAction(tr("Clear"), [&] { |
| 160 | emulated_device->SetRingParam({}); | 169 | emulated_controller->SetRingParam(param); |
| 161 | analog_map_buttons[sub_button_id]->setText(tr("[not set]")); | 170 | analog_map_buttons[sub_button_id]->setText(tr("[not set]")); |
| 162 | }); | 171 | }); |
| 163 | context_menu.addAction(tr("Invert axis"), [&] { | 172 | context_menu.addAction(tr("Invert axis"), [&] { |
| 164 | const bool invert_value = param.Get("invert_x", "+") == "-"; | 173 | const bool invert_value = param.Get("invert_x", "+") == "-"; |
| 165 | const std::string invert_str = invert_value ? "+" : "-"; | 174 | const std::string invert_str = invert_value ? "+" : "-"; |
| 166 | param.Set("invert_x", invert_str); | 175 | param.Set("invert_x", invert_str); |
| 167 | emulated_device->SetRingParam(param); | 176 | emulated_controller->SetRingParam(param); |
| 168 | for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM; | 177 | for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM; |
| 169 | ++sub_button_id2) { | 178 | ++sub_button_id2) { |
| 170 | analog_map_buttons[sub_button_id2]->setText( | 179 | analog_map_buttons[sub_button_id2]->setText( |
| @@ -177,16 +186,19 @@ ConfigureRingController::ConfigureRingController(QWidget* parent, | |||
| 177 | } | 186 | } |
| 178 | 187 | ||
| 179 | connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] { | 188 | connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] { |
| 180 | Common::ParamPackage param = emulated_device->GetRingParam(); | 189 | Common::ParamPackage param = emulated_controller->GetRingParam(); |
| 181 | const auto slider_value = ui->sliderRingAnalogDeadzone->value(); | 190 | const auto slider_value = ui->sliderRingAnalogDeadzone->value(); |
| 182 | ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value)); | 191 | ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value)); |
| 183 | param.Set("deadzone", slider_value / 100.0f); | 192 | param.Set("deadzone", slider_value / 100.0f); |
| 184 | emulated_device->SetRingParam(param); | 193 | emulated_controller->SetRingParam(param); |
| 185 | }); | 194 | }); |
| 186 | 195 | ||
| 187 | connect(ui->restore_defaults_button, &QPushButton::clicked, this, | 196 | connect(ui->restore_defaults_button, &QPushButton::clicked, this, |
| 188 | &ConfigureRingController::RestoreDefaults); | 197 | &ConfigureRingController::RestoreDefaults); |
| 189 | 198 | ||
| 199 | connect(ui->enable_ring_controller_button, &QPushButton::clicked, this, | ||
| 200 | &ConfigureRingController::EnableRingController); | ||
| 201 | |||
| 190 | timeout_timer->setSingleShot(true); | 202 | timeout_timer->setSingleShot(true); |
| 191 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); | 203 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); |
| 192 | 204 | ||
| @@ -202,7 +214,14 @@ ConfigureRingController::ConfigureRingController(QWidget* parent, | |||
| 202 | } | 214 | } |
| 203 | 215 | ||
| 204 | ConfigureRingController::~ConfigureRingController() { | 216 | ConfigureRingController::~ConfigureRingController() { |
| 205 | emulated_device->DisableConfiguration(); | 217 | emulated_controller->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 218 | Common::Input::PollingMode::Active); | ||
| 219 | emulated_controller->DisableConfiguration(); | ||
| 220 | |||
| 221 | if (is_controller_set) { | ||
| 222 | emulated_controller->DeleteCallback(callback_key); | ||
| 223 | is_controller_set = false; | ||
| 224 | } | ||
| 206 | }; | 225 | }; |
| 207 | 226 | ||
| 208 | void ConfigureRingController::changeEvent(QEvent* event) { | 227 | void ConfigureRingController::changeEvent(QEvent* event) { |
| @@ -219,7 +238,7 @@ void ConfigureRingController::RetranslateUI() { | |||
| 219 | 238 | ||
| 220 | void ConfigureRingController::UpdateUI() { | 239 | void ConfigureRingController::UpdateUI() { |
| 221 | RetranslateUI(); | 240 | RetranslateUI(); |
| 222 | const Common::ParamPackage param = emulated_device->GetRingParam(); | 241 | const Common::ParamPackage param = emulated_controller->GetRingParam(); |
| 223 | 242 | ||
| 224 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { | 243 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { |
| 225 | auto* const analog_button = analog_map_buttons[sub_button_id]; | 244 | auto* const analog_button = analog_map_buttons[sub_button_id]; |
| @@ -240,9 +259,9 @@ void ConfigureRingController::UpdateUI() { | |||
| 240 | } | 259 | } |
| 241 | 260 | ||
| 242 | void ConfigureRingController::ApplyConfiguration() { | 261 | void ConfigureRingController::ApplyConfiguration() { |
| 243 | emulated_device->DisableConfiguration(); | 262 | emulated_controller->DisableConfiguration(); |
| 244 | emulated_device->SaveCurrentConfig(); | 263 | emulated_controller->SaveCurrentConfig(); |
| 245 | emulated_device->EnableConfiguration(); | 264 | emulated_controller->EnableConfiguration(); |
| 246 | } | 265 | } |
| 247 | 266 | ||
| 248 | void ConfigureRingController::LoadConfiguration() { | 267 | void ConfigureRingController::LoadConfiguration() { |
| @@ -252,10 +271,62 @@ void ConfigureRingController::LoadConfiguration() { | |||
| 252 | void ConfigureRingController::RestoreDefaults() { | 271 | void ConfigureRingController::RestoreDefaults() { |
| 253 | const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( | 272 | const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( |
| 254 | 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); | 273 | 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); |
| 255 | emulated_device->SetRingParam(Common::ParamPackage(default_ring_string)); | 274 | emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string)); |
| 256 | UpdateUI(); | 275 | UpdateUI(); |
| 257 | } | 276 | } |
| 258 | 277 | ||
| 278 | void ConfigureRingController::EnableRingController() { | ||
| 279 | const auto dialog_title = tr("Error enabling ring input"); | ||
| 280 | |||
| 281 | is_ring_enabled = false; | ||
| 282 | ui->ring_controller_sensor_value->setText(tr("Not connected")); | ||
| 283 | |||
| 284 | if (!Settings::values.enable_joycon_driver) { | ||
| 285 | QMessageBox::warning(this, dialog_title, tr("Direct Joycon driver is not enabled")); | ||
| 286 | return; | ||
| 287 | } | ||
| 288 | |||
| 289 | ui->enable_ring_controller_button->setEnabled(false); | ||
| 290 | ui->enable_ring_controller_button->setText(tr("Configuring")); | ||
| 291 | // SetPollingMode is blocking. Allow to update the button status before calling the command | ||
| 292 | repaint(); | ||
| 293 | |||
| 294 | const auto result = emulated_controller->SetPollingMode( | ||
| 295 | Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Ring); | ||
| 296 | switch (result) { | ||
| 297 | case Common::Input::DriverResult::Success: | ||
| 298 | is_ring_enabled = true; | ||
| 299 | break; | ||
| 300 | case Common::Input::DriverResult::NotSupported: | ||
| 301 | QMessageBox::warning(this, dialog_title, | ||
| 302 | tr("The current mapped device doesn't support the ring controller")); | ||
| 303 | break; | ||
| 304 | case Common::Input::DriverResult::NoDeviceDetected: | ||
| 305 | QMessageBox::warning(this, dialog_title, | ||
| 306 | tr("The current mapped device doesn't have a ring attached")); | ||
| 307 | break; | ||
| 308 | default: | ||
| 309 | QMessageBox::warning(this, dialog_title, | ||
| 310 | tr("Unexpected driver result %1").arg(static_cast<int>(result))); | ||
| 311 | break; | ||
| 312 | } | ||
| 313 | ui->enable_ring_controller_button->setEnabled(true); | ||
| 314 | ui->enable_ring_controller_button->setText(tr("Enable")); | ||
| 315 | } | ||
| 316 | |||
| 317 | void ConfigureRingController::ControllerUpdate(Core::HID::ControllerTriggerType type) { | ||
| 318 | if (!is_ring_enabled) { | ||
| 319 | return; | ||
| 320 | } | ||
| 321 | if (type != Core::HID::ControllerTriggerType::RingController) { | ||
| 322 | return; | ||
| 323 | } | ||
| 324 | |||
| 325 | const auto value = emulated_controller->GetRingSensorValues(); | ||
| 326 | const auto tex_value = QString::fromStdString(fmt::format("{:.3f}", value.raw_value)); | ||
| 327 | ui->ring_controller_sensor_value->setText(tex_value); | ||
| 328 | } | ||
| 329 | |||
| 259 | void ConfigureRingController::HandleClick( | 330 | void ConfigureRingController::HandleClick( |
| 260 | QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, | 331 | QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, |
| 261 | InputCommon::Polling::InputType type) { | 332 | InputCommon::Polling::InputType type) { |
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h index 38a9cb716..b23c27906 100644 --- a/src/yuzu/configuration/configure_ringcon.h +++ b/src/yuzu/configuration/configure_ringcon.h | |||
| @@ -13,7 +13,7 @@ class InputSubsystem; | |||
| 13 | 13 | ||
| 14 | namespace Core::HID { | 14 | namespace Core::HID { |
| 15 | class HIDCore; | 15 | class HIDCore; |
| 16 | class EmulatedDevices; | 16 | class EmulatedController; |
| 17 | } // namespace Core::HID | 17 | } // namespace Core::HID |
| 18 | 18 | ||
| 19 | namespace Ui { | 19 | namespace Ui { |
| @@ -42,6 +42,12 @@ private: | |||
| 42 | /// Restore all buttons to their default values. | 42 | /// Restore all buttons to their default values. |
| 43 | void RestoreDefaults(); | 43 | void RestoreDefaults(); |
| 44 | 44 | ||
| 45 | /// Sets current polling mode to ring input | ||
| 46 | void EnableRingController(); | ||
| 47 | |||
| 48 | // Handles emulated controller events | ||
| 49 | void ControllerUpdate(Core::HID::ControllerTriggerType type); | ||
| 50 | |||
| 45 | /// Called when the button was pressed. | 51 | /// Called when the button was pressed. |
| 46 | void HandleClick(QPushButton* button, | 52 | void HandleClick(QPushButton* button, |
| 47 | std::function<void(const Common::ParamPackage&)> new_input_setter, | 53 | std::function<void(const Common::ParamPackage&)> new_input_setter, |
| @@ -78,7 +84,11 @@ private: | |||
| 78 | std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; | 84 | std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; |
| 79 | 85 | ||
| 80 | InputCommon::InputSubsystem* input_subsystem; | 86 | InputCommon::InputSubsystem* input_subsystem; |
| 81 | Core::HID::EmulatedDevices* emulated_device; | 87 | Core::HID::EmulatedController* emulated_controller; |
| 88 | |||
| 89 | bool is_ring_enabled{}; | ||
| 90 | bool is_controller_set{}; | ||
| 91 | int callback_key; | ||
| 82 | 92 | ||
| 83 | std::unique_ptr<Ui::ConfigureRingController> ui; | 93 | std::unique_ptr<Ui::ConfigureRingController> ui; |
| 84 | }; | 94 | }; |
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui index 9ec634dd4..514dff372 100644 --- a/src/yuzu/configuration/configure_ringcon.ui +++ b/src/yuzu/configuration/configure_ringcon.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>298</width> | 9 | <width>315</width> |
| 10 | <height>339</height> | 10 | <height>400</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -46,187 +46,283 @@ | |||
| 46 | </property> | 46 | </property> |
| 47 | </spacer> | 47 | </spacer> |
| 48 | </item> | 48 | </item> |
| 49 | <item> | 49 | <item> |
| 50 | <widget class="QGroupBox" name="RingAnalog"> | 50 | <widget class="QGroupBox" name="RingAnalog"> |
| 51 | <property name="title"> | 51 | <property name="title"> |
| 52 | <string>Ring Sensor Parameters</string> | 52 | <string>Virtual Ring Sensor Parameters</string> |
| 53 | </property> | 53 | </property> |
| 54 | <property name="alignment"> | 54 | <property name="alignment"> |
| 55 | <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> | 55 | <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> |
| 56 | </property> | 56 | </property> |
| 57 | <layout class="QVBoxLayout" name="verticalLayout_3"> | 57 | <layout class="QVBoxLayout" name="verticalLayout_1"> |
| 58 | <property name="spacing"> | ||
| 59 | <number>0</number> | ||
| 60 | </property> | ||
| 61 | <property name="sizeConstraint"> | ||
| 62 | <enum>QLayout::SetDefaultConstraint</enum> | ||
| 63 | </property> | ||
| 64 | <property name="leftMargin"> | ||
| 65 | <number>3</number> | ||
| 66 | </property> | ||
| 67 | <property name="topMargin"> | ||
| 68 | <number>6</number> | ||
| 69 | </property> | ||
| 70 | <property name="rightMargin"> | ||
| 71 | <number>3</number> | ||
| 72 | </property> | ||
| 73 | <property name="bottomMargin"> | ||
| 74 | <number>0</number> | ||
| 75 | </property> | ||
| 76 | <item> | ||
| 77 | <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout"> | ||
| 78 | <property name="spacing"> | 58 | <property name="spacing"> |
| 79 | <number>3</number> | 59 | <number>0</number> |
| 80 | </property> | 60 | </property> |
| 81 | <item alignment="Qt::AlignHCenter"> | 61 | <property name="sizeConstraint"> |
| 82 | <widget class="QGroupBox" name="buttonRingAnalogPullGroup"> | 62 | <enum>QLayout::SetDefaultConstraint</enum> |
| 83 | <property name="title"> | 63 | </property> |
| 84 | <string>Pull</string> | 64 | <property name="leftMargin"> |
| 85 | </property> | 65 | <number>3</number> |
| 86 | <property name="alignment"> | 66 | </property> |
| 87 | <set>Qt::AlignCenter</set> | 67 | <property name="topMargin"> |
| 68 | <number>6</number> | ||
| 69 | </property> | ||
| 70 | <property name="rightMargin"> | ||
| 71 | <number>3</number> | ||
| 72 | </property> | ||
| 73 | <property name="bottomMargin"> | ||
| 74 | <number>0</number> | ||
| 75 | </property> | ||
| 76 | <item> | ||
| 77 | <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout"> | ||
| 78 | <property name="spacing"> | ||
| 79 | <number>3</number> | ||
| 88 | </property> | 80 | </property> |
| 89 | <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout"> | 81 | <item alignment="Qt::AlignHCenter"> |
| 90 | <property name="spacing"> | 82 | <widget class="QGroupBox" name="buttonRingAnalogPullGroup"> |
| 91 | <number>3</number> | 83 | <property name="title"> |
| 84 | <string>Pull</string> | ||
| 92 | </property> | 85 | </property> |
| 93 | <property name="leftMargin"> | 86 | <property name="alignment"> |
| 94 | <number>3</number> | 87 | <set>Qt::AlignCenter</set> |
| 95 | </property> | ||
| 96 | <property name="topMargin"> | ||
| 97 | <number>3</number> | ||
| 98 | </property> | 88 | </property> |
| 99 | <property name="rightMargin"> | 89 | <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout"> |
| 100 | <number>3</number> | 90 | <property name="spacing"> |
| 91 | <number>3</number> | ||
| 92 | </property> | ||
| 93 | <property name="leftMargin"> | ||
| 94 | <number>3</number> | ||
| 95 | </property> | ||
| 96 | <property name="topMargin"> | ||
| 97 | <number>3</number> | ||
| 98 | </property> | ||
| 99 | <property name="rightMargin"> | ||
| 100 | <number>3</number> | ||
| 101 | </property> | ||
| 102 | <property name="bottomMargin"> | ||
| 103 | <number>3</number> | ||
| 104 | </property> | ||
| 105 | <item> | ||
| 106 | <widget class="QPushButton" name="buttonRingAnalogPull"> | ||
| 107 | <property name="minimumSize"> | ||
| 108 | <size> | ||
| 109 | <width>70</width> | ||
| 110 | <height>0</height> | ||
| 111 | </size> | ||
| 112 | </property> | ||
| 113 | <property name="maximumSize"> | ||
| 114 | <size> | ||
| 115 | <width>68</width> | ||
| 116 | <height>16777215</height> | ||
| 117 | </size> | ||
| 118 | </property> | ||
| 119 | <property name="styleSheet"> | ||
| 120 | <string notr="true">min-width: 68px;</string> | ||
| 121 | </property> | ||
| 122 | <property name="text"> | ||
| 123 | <string>Pull</string> | ||
| 124 | </property> | ||
| 125 | </widget> | ||
| 126 | </item> | ||
| 127 | </layout> | ||
| 128 | </widget> | ||
| 129 | </item> | ||
| 130 | <item alignment="Qt::AlignHCenter"> | ||
| 131 | <widget class="QGroupBox" name="buttonRingAnalogPushGroup"> | ||
| 132 | <property name="title"> | ||
| 133 | <string>Push</string> | ||
| 101 | </property> | 134 | </property> |
| 102 | <property name="bottomMargin"> | 135 | <property name="alignment"> |
| 103 | <number>3</number> | 136 | <set>Qt::AlignCenter</set> |
| 104 | </property> | 137 | </property> |
| 105 | <item> | 138 | <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout"> |
| 106 | <widget class="QPushButton" name="buttonRingAnalogPull"> | 139 | <property name="spacing"> |
| 107 | <property name="minimumSize"> | 140 | <number>3</number> |
| 108 | <size> | 141 | </property> |
| 109 | <width>68</width> | 142 | <property name="leftMargin"> |
| 110 | <height>0</height> | 143 | <number>3</number> |
| 111 | </size> | 144 | </property> |
| 112 | </property> | 145 | <property name="topMargin"> |
| 113 | <property name="maximumSize"> | 146 | <number>3</number> |
| 114 | <size> | 147 | </property> |
| 115 | <width>68</width> | 148 | <property name="rightMargin"> |
| 116 | <height>16777215</height> | 149 | <number>3</number> |
| 117 | </size> | 150 | </property> |
| 118 | </property> | 151 | <property name="bottomMargin"> |
| 119 | <property name="styleSheet"> | 152 | <number>3</number> |
| 120 | <string notr="true">min-width: 68px;</string> | 153 | </property> |
| 121 | </property> | 154 | <item> |
| 122 | <property name="text"> | 155 | <widget class="QPushButton" name="buttonRingAnalogPush"> |
| 123 | <string>Pull</string> | 156 | <property name="minimumSize"> |
| 124 | </property> | 157 | <size> |
| 125 | </widget> | 158 | <width>70</width> |
| 126 | </item> | 159 | <height>0</height> |
| 127 | </layout> | 160 | </size> |
| 128 | </widget> | 161 | </property> |
| 162 | <property name="maximumSize"> | ||
| 163 | <size> | ||
| 164 | <width>68</width> | ||
| 165 | <height>16777215</height> | ||
| 166 | </size> | ||
| 167 | </property> | ||
| 168 | <property name="styleSheet"> | ||
| 169 | <string notr="true">min-width: 68px;</string> | ||
| 170 | </property> | ||
| 171 | <property name="text"> | ||
| 172 | <string>Push</string> | ||
| 173 | </property> | ||
| 174 | </widget> | ||
| 175 | </item> | ||
| 176 | </layout> | ||
| 177 | </widget> | ||
| 178 | </item> | ||
| 179 | </layout> | ||
| 129 | </item> | 180 | </item> |
| 130 | <item alignment="Qt::AlignHCenter"> | 181 | <item> |
| 131 | <widget class="QGroupBox" name="buttonRingAnalogPushGroup"> | 182 | <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout"> |
| 132 | <property name="title"> | 183 | <property name="spacing"> |
| 133 | <string>Push</string> | 184 | <number>3</number> |
| 134 | </property> | 185 | </property> |
| 135 | <property name="alignment"> | 186 | <property name="sizeConstraint"> |
| 136 | <set>Qt::AlignCenter</set> | 187 | <enum>QLayout::SetDefaultConstraint</enum> |
| 137 | </property> | 188 | </property> |
| 138 | <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout"> | 189 | <property name="leftMargin"> |
| 139 | <property name="spacing"> | 190 | <number>0</number> |
| 140 | <number>3</number> | 191 | </property> |
| 141 | </property> | 192 | <property name="topMargin"> |
| 142 | <property name="leftMargin"> | 193 | <number>10</number> |
| 143 | <number>3</number> | 194 | </property> |
| 144 | </property> | 195 | <property name="rightMargin"> |
| 145 | <property name="topMargin"> | 196 | <number>0</number> |
| 146 | <number>3</number> | 197 | </property> |
| 147 | </property> | 198 | <property name="bottomMargin"> |
| 148 | <property name="rightMargin"> | 199 | <number>3</number> |
| 149 | <number>3</number> | 200 | </property> |
| 150 | </property> | 201 | <item> |
| 151 | <property name="bottomMargin"> | 202 | <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout"> |
| 152 | <number>3</number> | ||
| 153 | </property> | ||
| 154 | <item> | 203 | <item> |
| 155 | <widget class="QPushButton" name="buttonRingAnalogPush"> | 204 | <widget class="QLabel" name="labelRingAnalogDeadzone"> |
| 156 | <property name="minimumSize"> | ||
| 157 | <size> | ||
| 158 | <width>68</width> | ||
| 159 | <height>0</height> | ||
| 160 | </size> | ||
| 161 | </property> | ||
| 162 | <property name="maximumSize"> | ||
| 163 | <size> | ||
| 164 | <width>68</width> | ||
| 165 | <height>16777215</height> | ||
| 166 | </size> | ||
| 167 | </property> | ||
| 168 | <property name="styleSheet"> | ||
| 169 | <string notr="true">min-width: 68px;</string> | ||
| 170 | </property> | ||
| 171 | <property name="text"> | 205 | <property name="text"> |
| 172 | <string>Push</string> | 206 | <string>Deadzone: 0%</string> |
| 207 | </property> | ||
| 208 | <property name="alignment"> | ||
| 209 | <set>Qt::AlignHCenter</set> | ||
| 173 | </property> | 210 | </property> |
| 174 | </widget> | 211 | </widget> |
| 175 | </item> | 212 | </item> |
| 176 | </layout> | 213 | </layout> |
| 177 | </widget> | 214 | </item> |
| 215 | <item> | ||
| 216 | <widget class="QSlider" name="sliderRingAnalogDeadzone"> | ||
| 217 | <property name="maximum"> | ||
| 218 | <number>100</number> | ||
| 219 | </property> | ||
| 220 | <property name="orientation"> | ||
| 221 | <enum>Qt::Horizontal</enum> | ||
| 222 | </property> | ||
| 223 | </widget> | ||
| 224 | </item> | ||
| 225 | </layout> | ||
| 178 | </item> | 226 | </item> |
| 179 | </layout> | 227 | </layout> |
| 180 | </item> | 228 | </widget> |
| 181 | <item> | 229 | </item> |
| 182 | <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout"> | 230 | <item> |
| 231 | <widget class="QGroupBox" name="RingDriver"> | ||
| 232 | <property name="title"> | ||
| 233 | <string>Direct Joycon Driver</string> | ||
| 234 | </property> | ||
| 235 | <property name="alignment"> | ||
| 236 | <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> | ||
| 237 | </property> | ||
| 238 | <layout class="QVBoxLayout" name="verticalLayout_2"> | ||
| 183 | <property name="spacing"> | 239 | <property name="spacing"> |
| 184 | <number>3</number> | 240 | <number>0</number> |
| 185 | </property> | 241 | </property> |
| 186 | <property name="sizeConstraint"> | 242 | <property name="sizeConstraint"> |
| 187 | <enum>QLayout::SetDefaultConstraint</enum> | 243 | <enum>QLayout::SetDefaultConstraint</enum> |
| 188 | </property> | 244 | </property> |
| 189 | <property name="leftMargin"> | 245 | <property name="leftMargin"> |
| 190 | <number>0</number> | 246 | <number>3</number> |
| 191 | </property> | 247 | </property> |
| 192 | <property name="topMargin"> | 248 | <property name="topMargin"> |
| 193 | <number>10</number> | 249 | <number>6</number> |
| 194 | </property> | 250 | </property> |
| 195 | <property name="rightMargin"> | 251 | <property name="rightMargin"> |
| 196 | <number>0</number> | 252 | <number>3</number> |
| 197 | </property> | 253 | </property> |
| 198 | <property name="bottomMargin"> | 254 | <property name="bottomMargin"> |
| 199 | <number>3</number> | 255 | <number>10</number> |
| 200 | </property> | 256 | </property> |
| 201 | <item> | 257 | <item> |
| 202 | <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout"> | 258 | <layout class="QGridLayout" name="gridLayout"> |
| 203 | <item> | 259 | <property name="leftMargin"> |
| 204 | <widget class="QLabel" name="labelRingAnalogDeadzone"> | 260 | <number>10</number> |
| 261 | </property> | ||
| 262 | <property name="topMargin"> | ||
| 263 | <number>6</number> | ||
| 264 | </property> | ||
| 265 | <property name="rightMargin"> | ||
| 266 | <number>10</number> | ||
| 267 | </property> | ||
| 268 | <property name="bottomMargin"> | ||
| 269 | <number>10</number> | ||
| 270 | </property> | ||
| 271 | <property name="verticalSpacing"> | ||
| 272 | <number>10</number> | ||
| 273 | </property> | ||
| 274 | <item row="0" column="1"> | ||
| 275 | <spacer name="horizontalSpacer"> | ||
| 276 | <property name="orientation"> | ||
| 277 | <enum>Qt::Horizontal</enum> | ||
| 278 | </property> | ||
| 279 | <property name="sizeType"> | ||
| 280 | <enum>QSizePolicy::Fixed</enum> | ||
| 281 | </property> | ||
| 282 | <property name="sizeHint" stdset="0"> | ||
| 283 | <size> | ||
| 284 | <width>76</width> | ||
| 285 | <height>20</height> | ||
| 286 | </size> | ||
| 287 | </property> | ||
| 288 | </spacer> | ||
| 289 | </item> | ||
| 290 | <item row="0" column="0"> | ||
| 291 | <widget class="QLabel" name="enable_ring_controller_label"> | ||
| 292 | <property name="text"> | ||
| 293 | <string>Enable Ring Input</string> | ||
| 294 | </property> | ||
| 295 | </widget> | ||
| 296 | </item> | ||
| 297 | <item row="0" column="2"> | ||
| 298 | <widget class="QPushButton" name="enable_ring_controller_button"> | ||
| 205 | <property name="text"> | 299 | <property name="text"> |
| 206 | <string>Deadzone: 0%</string> | 300 | <string>Enable</string> |
| 301 | </property> | ||
| 302 | </widget> | ||
| 303 | </item> | ||
| 304 | <item row="1" column="0"> | ||
| 305 | <widget class="QLabel" name="ring_controller_sensor_label"> | ||
| 306 | <property name="text"> | ||
| 307 | <string>Ring Sensor Value</string> | ||
| 308 | </property> | ||
| 309 | </widget> | ||
| 310 | </item> | ||
| 311 | <item row="1" column="2"> | ||
| 312 | <widget class="QLabel" name="ring_controller_sensor_value"> | ||
| 313 | <property name="text"> | ||
| 314 | <string>Not connected</string> | ||
| 207 | </property> | 315 | </property> |
| 208 | <property name="alignment"> | 316 | <property name="alignment"> |
| 209 | <set>Qt::AlignHCenter</set> | 317 | <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> |
| 210 | </property> | 318 | </property> |
| 211 | </widget> | 319 | </widget> |
| 212 | </item> | 320 | </item> |
| 213 | </layout> | 321 | </layout> |
| 214 | </item> | ||
| 215 | <item> | ||
| 216 | <widget class="QSlider" name="sliderRingAnalogDeadzone"> | ||
| 217 | <property name="maximum"> | ||
| 218 | <number>100</number> | ||
| 219 | </property> | ||
| 220 | <property name="orientation"> | ||
| 221 | <enum>Qt::Horizontal</enum> | ||
| 222 | </property> | ||
| 223 | </widget> | ||
| 224 | </item> | 322 | </item> |
| 225 | </layout> | 323 | </layout> |
| 226 | </item> | 324 | </widget> |
| 227 | </layout> | 325 | </item> |
| 228 | </widget> | ||
| 229 | </item> | ||
| 230 | <item> | 326 | <item> |
| 231 | <spacer name="verticalSpacer"> | 327 | <spacer name="verticalSpacer"> |
| 232 | <property name="orientation"> | 328 | <property name="orientation"> |
| @@ -273,6 +369,6 @@ | |||
| 273 | <signal>rejected()</signal> | 369 | <signal>rejected()</signal> |
| 274 | <receiver>ConfigureRingController</receiver> | 370 | <receiver>ConfigureRingController</receiver> |
| 275 | <slot>reject()</slot> | 371 | <slot>reject()</slot> |
| 276 | </connection> | 372 | </connection> |
| 277 | </connections> | 373 | </connections> |
| 278 | </ui> | 374 | </ui> |
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp index 1edc5f1f3..5a545aa70 100644 --- a/src/yuzu/configuration/configure_tas.cpp +++ b/src/yuzu/configuration/configure_tas.cpp | |||
| @@ -17,7 +17,6 @@ ConfigureTasDialog::ConfigureTasDialog(QWidget* parent) | |||
| 17 | 17 | ||
| 18 | setFocusPolicy(Qt::ClickFocus); | 18 | setFocusPolicy(Qt::ClickFocus); |
| 19 | setWindowTitle(tr("TAS Configuration")); | 19 | setWindowTitle(tr("TAS Configuration")); |
| 20 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 21 | 20 | ||
| 22 | connect(ui->tas_path_button, &QToolButton::pressed, this, | 21 | connect(ui->tas_path_button, &QToolButton::pressed, this, |
| 23 | [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); }); | 22 | [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); }); |
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp index 19f3775a3..e2f55ebae 100644 --- a/src/yuzu/debugger/controller.cpp +++ b/src/yuzu/debugger/controller.cpp | |||
| @@ -20,9 +20,8 @@ ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_, | |||
| 20 | setWindowTitle(tr("Controller P1")); | 20 | setWindowTitle(tr("Controller P1")); |
| 21 | resize(500, 350); | 21 | resize(500, 350); |
| 22 | setMinimumSize(500, 350); | 22 | setMinimumSize(500, 350); |
| 23 | // Remove the "?" button from the titlebar and enable the maximize button | 23 | // Enable the maximize button |
| 24 | setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | | 24 | setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint); |
| 25 | Qt::WindowMaximizeButtonHint); | ||
| 26 | 25 | ||
| 27 | widget = new PlayerControlPreview(this); | 26 | widget = new PlayerControlPreview(this); |
| 28 | refreshConfiguration(); | 27 | refreshConfiguration(); |
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp index d3e2d3c12..493ee0b17 100644 --- a/src/yuzu/debugger/profiler.cpp +++ b/src/yuzu/debugger/profiler.cpp | |||
| @@ -49,9 +49,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Di | |||
| 49 | setObjectName(QStringLiteral("MicroProfile")); | 49 | setObjectName(QStringLiteral("MicroProfile")); |
| 50 | setWindowTitle(tr("&MicroProfile")); | 50 | setWindowTitle(tr("&MicroProfile")); |
| 51 | resize(1000, 600); | 51 | resize(1000, 600); |
| 52 | // Remove the "?" button from the titlebar and enable the maximize button | 52 | // Enable the maximize button |
| 53 | setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | | 53 | setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint); |
| 54 | Qt::WindowMaximizeButtonHint); | ||
| 55 | 54 | ||
| 56 | #if MICROPROFILE_ENABLED | 55 | #if MICROPROFILE_ENABLED |
| 57 | 56 | ||
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp index 84ec4fe13..673bbaa83 100644 --- a/src/yuzu/install_dialog.cpp +++ b/src/yuzu/install_dialog.cpp | |||
| @@ -46,7 +46,6 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo | |||
| 46 | vbox_layout->addLayout(hbox_layout); | 46 | vbox_layout->addLayout(hbox_layout); |
| 47 | 47 | ||
| 48 | setLayout(vbox_layout); | 48 | setLayout(vbox_layout); |
| 49 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 50 | setWindowTitle(tr("Install Files to NAND")); | 49 | setWindowTitle(tr("Install Files to NAND")); |
| 51 | } | 50 | } |
| 52 | 51 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 571eacf9f..62aaf41bf 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -2758,8 +2758,7 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 2758 | ui->action_Install_File_NAND->setEnabled(false); | 2758 | ui->action_Install_File_NAND->setEnabled(false); |
| 2759 | 2759 | ||
| 2760 | install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); | 2760 | install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); |
| 2761 | install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & | 2761 | install_progress->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint); |
| 2762 | ~Qt::WindowMaximizeButtonHint); | ||
| 2763 | install_progress->setAttribute(Qt::WA_DeleteOnClose, true); | 2762 | install_progress->setAttribute(Qt::WA_DeleteOnClose, true); |
| 2764 | install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40); | 2763 | install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40); |
| 2765 | install_progress->show(); | 2764 | install_progress->show(); |
| @@ -4456,6 +4455,11 @@ int main(int argc, char* argv[]) { | |||
| 4456 | } | 4455 | } |
| 4457 | #endif | 4456 | #endif |
| 4458 | 4457 | ||
| 4458 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||
| 4459 | // Disables the "?" button on all dialogs. Disabled by default on Qt6. | ||
| 4460 | QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); | ||
| 4461 | #endif | ||
| 4462 | |||
| 4459 | // Enables the core to make the qt created contexts current on std::threads | 4463 | // Enables the core to make the qt created contexts current on std::threads |
| 4460 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 4464 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 4461 | QApplication app(argc, argv); | 4465 | QApplication app(argc, argv); |
diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp index bbb370595..5f6a9c193 100644 --- a/src/yuzu/util/limitable_input_dialog.cpp +++ b/src/yuzu/util/limitable_input_dialog.cpp | |||
| @@ -16,8 +16,6 @@ LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} { | |||
| 16 | LimitableInputDialog::~LimitableInputDialog() = default; | 16 | LimitableInputDialog::~LimitableInputDialog() = default; |
| 17 | 17 | ||
| 18 | void LimitableInputDialog::CreateUI() { | 18 | void LimitableInputDialog::CreateUI() { |
| 19 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 20 | |||
| 21 | text_label = new QLabel(this); | 19 | text_label = new QLabel(this); |
| 22 | text_entry = new QLineEdit(this); | 20 | text_entry = new QLineEdit(this); |
| 23 | text_label_invalid = new QLabel(this); | 21 | text_label_invalid = new QLabel(this); |
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp index 4b10fa517..1670aa596 100644 --- a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp +++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { | 9 | SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { |
| 10 | setWindowTitle(tr("Enter a hotkey")); | 10 | setWindowTitle(tr("Enter a hotkey")); |
| 11 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 12 | 11 | ||
| 13 | key_sequence = new QKeySequenceEdit; | 12 | key_sequence = new QKeySequenceEdit; |
| 14 | 13 | ||
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 6fcf04e1b..67d230462 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | namespace DefaultINI { | 6 | namespace DefaultINI { |
| 7 | 7 | ||
| 8 | const char* sdl2_config_file = R"( | 8 | const char* sdl2_config_file = |
| 9 | 9 | R"( | |
| 10 | [ControlsP0] | 10 | [ControlsP0] |
| 11 | # The input devices and parameters for each Switch native input | 11 | # The input devices and parameters for each Switch native input |
| 12 | # The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... | 12 | # The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... |
| @@ -143,6 +143,8 @@ mouse_enabled = | |||
| 143 | # 0 (default): Disabled, 1: Enabled | 143 | # 0 (default): Disabled, 1: Enabled |
| 144 | keyboard_enabled = | 144 | keyboard_enabled = |
| 145 | 145 | ||
| 146 | )" | ||
| 147 | R"( | ||
| 146 | [Core] | 148 | [Core] |
| 147 | # Whether to use multi-core for CPU emulation | 149 | # Whether to use multi-core for CPU emulation |
| 148 | # 0: Disabled, 1 (default): Enabled | 150 | # 0: Disabled, 1 (default): Enabled |
| @@ -242,6 +244,8 @@ cpuopt_unsafe_fastmem_check = | |||
| 242 | # 0: Disabled, 1 (default): Enabled | 244 | # 0: Disabled, 1 (default): Enabled |
| 243 | cpuopt_unsafe_ignore_global_monitor = | 245 | cpuopt_unsafe_ignore_global_monitor = |
| 244 | 246 | ||
| 247 | )" | ||
| 248 | R"( | ||
| 245 | [Renderer] | 249 | [Renderer] |
| 246 | # Which backend API to use. | 250 | # Which backend API to use. |
| 247 | # 0: OpenGL, 1 (default): Vulkan | 251 | # 0: OpenGL, 1 (default): Vulkan |
| @@ -360,6 +364,8 @@ bg_red = | |||
| 360 | bg_blue = | 364 | bg_blue = |
| 361 | bg_green = | 365 | bg_green = |
| 362 | 366 | ||
| 367 | )" | ||
| 368 | R"( | ||
| 363 | [Audio] | 369 | [Audio] |
| 364 | # Which audio output engine to use. | 370 | # Which audio output engine to use. |
| 365 | # auto (default): Auto-select | 371 | # auto (default): Auto-select |
diff --git a/vcpkg.json b/vcpkg.json index 3c92510d6..ef271f778 100644 --- a/vcpkg.json +++ b/vcpkg.json | |||
| @@ -40,7 +40,7 @@ | |||
| 40 | "overrides": [ | 40 | "overrides": [ |
| 41 | { | 41 | { |
| 42 | "name": "catch2", | 42 | "name": "catch2", |
| 43 | "version": "2.13.9" | 43 | "version": "3.0.1" |
| 44 | }, | 44 | }, |
| 45 | { | 45 | { |
| 46 | "name": "fmt", | 46 | "name": "fmt", |