diff options
| author | 2024-01-04 20:37:43 -0600 | |
|---|---|---|
| committer | 2024-01-05 11:41:15 -0600 | |
| commit | ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch) | |
| tree | 3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/hid_core/resources | |
| parent | Merge pull request #12437 from ameerj/gl-amd-fixes (diff) | |
| download | yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.gz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.xz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.zip | |
hid_core: Move hid to it's own subproject
Diffstat (limited to 'src/hid_core/resources')
53 files changed, 6773 insertions, 0 deletions
diff --git a/src/hid_core/resources/applet_resource.cpp b/src/hid_core/resources/applet_resource.cpp new file mode 100644 index 000000000..d16cff1a4 --- /dev/null +++ b/src/hid_core/resources/applet_resource.cpp | |||
| @@ -0,0 +1,329 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 6 | #include "hid_core/hid_result.h" | ||
| 7 | #include "hid_core/resources/applet_resource.h" | ||
| 8 | #include "hid_core/resources/shared_memory_format.h" | ||
| 9 | |||
| 10 | namespace Service::HID { | ||
| 11 | |||
| 12 | AppletResource::AppletResource(Core::System& system_) : system{system_} {} | ||
| 13 | |||
| 14 | AppletResource::~AppletResource() = default; | ||
| 15 | |||
| 16 | Result AppletResource::CreateAppletResource(u64 aruid) { | ||
| 17 | const u64 index = GetIndexFromAruid(aruid); | ||
| 18 | |||
| 19 | if (index >= AruidIndexMax) { | ||
| 20 | return ResultAruidNotRegistered; | ||
| 21 | } | ||
| 22 | |||
| 23 | if (data[index].flag.is_assigned) { | ||
| 24 | return ResultAruidAlreadyRegistered; | ||
| 25 | } | ||
| 26 | |||
| 27 | auto& shared_memory = shared_memory_holder[index]; | ||
| 28 | if (!shared_memory.IsMapped()) { | ||
| 29 | const Result result = shared_memory.Initialize(system); | ||
| 30 | if (result.IsError()) { | ||
| 31 | return result; | ||
| 32 | } | ||
| 33 | if (shared_memory.GetAddress() == nullptr) { | ||
| 34 | shared_memory.Finalize(); | ||
| 35 | return ResultSharedMemoryNotInitialized; | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | auto* shared_memory_format = shared_memory.GetAddress(); | ||
| 40 | if (shared_memory_format != nullptr) { | ||
| 41 | shared_memory_format->Initialize(); | ||
| 42 | } | ||
| 43 | |||
| 44 | data[index].shared_memory_format = shared_memory_format; | ||
| 45 | data[index].flag.is_assigned.Assign(true); | ||
| 46 | // TODO: InitializeSixAxisControllerConfig(false); | ||
| 47 | active_aruid = aruid; | ||
| 48 | return ResultSuccess; | ||
| 49 | } | ||
| 50 | |||
| 51 | Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input) { | ||
| 52 | const u64 index = GetIndexFromAruid(aruid); | ||
| 53 | |||
| 54 | if (index < AruidIndexMax) { | ||
| 55 | return ResultAruidAlreadyRegistered; | ||
| 56 | } | ||
| 57 | |||
| 58 | std::size_t data_index = AruidIndexMax; | ||
| 59 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 60 | if (!data[i].flag.is_initialized) { | ||
| 61 | data_index = i; | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | if (data_index == AruidIndexMax) { | ||
| 67 | return ResultAruidNoAvailableEntries; | ||
| 68 | } | ||
| 69 | |||
| 70 | AruidData& aruid_data = data[data_index]; | ||
| 71 | |||
| 72 | aruid_data.aruid = aruid; | ||
| 73 | aruid_data.flag.is_initialized.Assign(true); | ||
| 74 | if (enable_input) { | ||
| 75 | aruid_data.flag.enable_pad_input.Assign(true); | ||
| 76 | aruid_data.flag.enable_six_axis_sensor.Assign(true); | ||
| 77 | aruid_data.flag.bit_18.Assign(true); | ||
| 78 | aruid_data.flag.enable_touchscreen.Assign(true); | ||
| 79 | } | ||
| 80 | |||
| 81 | data_index = AruidIndexMax; | ||
| 82 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 83 | if (registration_list.flag[i] == RegistrationStatus::Initialized) { | ||
| 84 | if (registration_list.aruid[i] != aruid) { | ||
| 85 | continue; | ||
| 86 | } | ||
| 87 | data_index = i; | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | if (registration_list.flag[i] == RegistrationStatus::None) { | ||
| 91 | data_index = i; | ||
| 92 | break; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | if (data_index == AruidIndexMax) { | ||
| 97 | return ResultSuccess; | ||
| 98 | } | ||
| 99 | |||
| 100 | registration_list.flag[data_index] = RegistrationStatus::Initialized; | ||
| 101 | registration_list.aruid[data_index] = aruid; | ||
| 102 | |||
| 103 | return ResultSuccess; | ||
| 104 | } | ||
| 105 | |||
| 106 | void AppletResource::UnregisterAppletResourceUserId(u64 aruid) { | ||
| 107 | u64 index = GetIndexFromAruid(aruid); | ||
| 108 | |||
| 109 | if (index < AruidIndexMax) { | ||
| 110 | if (data[index].flag.is_assigned) { | ||
| 111 | data[index].shared_memory_format = nullptr; | ||
| 112 | data[index].flag.is_assigned.Assign(false); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | index = GetIndexFromAruid(aruid); | ||
| 117 | if (index < AruidIndexMax) { | ||
| 118 | DestroySevenSixAxisTransferMemory(); | ||
| 119 | data[index].flag.raw = 0; | ||
| 120 | data[index].aruid = 0; | ||
| 121 | |||
| 122 | index = GetIndexFromAruid(aruid); | ||
| 123 | if (index < AruidIndexMax) { | ||
| 124 | registration_list.flag[index] = RegistrationStatus::PendingDelete; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | void AppletResource::FreeAppletResourceId(u64 aruid) { | ||
| 130 | u64 index = GetIndexFromAruid(aruid); | ||
| 131 | if (index >= AruidIndexMax) { | ||
| 132 | return; | ||
| 133 | } | ||
| 134 | |||
| 135 | auto& aruid_data = data[index]; | ||
| 136 | if (aruid_data.flag.is_assigned) { | ||
| 137 | aruid_data.shared_memory_format = nullptr; | ||
| 138 | aruid_data.flag.is_assigned.Assign(false); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | u64 AppletResource::GetActiveAruid() { | ||
| 143 | return active_aruid; | ||
| 144 | } | ||
| 145 | |||
| 146 | Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { | ||
| 147 | u64 index = GetIndexFromAruid(aruid); | ||
| 148 | if (index >= AruidIndexMax) { | ||
| 149 | return ResultAruidNotRegistered; | ||
| 150 | } | ||
| 151 | |||
| 152 | *out_handle = shared_memory_holder[index].GetHandle(); | ||
| 153 | return ResultSuccess; | ||
| 154 | } | ||
| 155 | |||
| 156 | Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, | ||
| 157 | u64 aruid) { | ||
| 158 | u64 index = GetIndexFromAruid(aruid); | ||
| 159 | if (index >= AruidIndexMax) { | ||
| 160 | return ResultAruidNotRegistered; | ||
| 161 | } | ||
| 162 | |||
| 163 | *out_shared_memory_format = data[index].shared_memory_format; | ||
| 164 | return ResultSuccess; | ||
| 165 | } | ||
| 166 | |||
| 167 | AruidData* AppletResource::GetAruidData(u64 aruid) { | ||
| 168 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 169 | if (aruid_index == AruidIndexMax) { | ||
| 170 | return nullptr; | ||
| 171 | } | ||
| 172 | return &data[aruid_index]; | ||
| 173 | } | ||
| 174 | |||
| 175 | AruidData* AppletResource::GetAruidDataByIndex(std::size_t aruid_index) { | ||
| 176 | return &data[aruid_index]; | ||
| 177 | } | ||
| 178 | |||
| 179 | bool AppletResource::IsVibrationAruidActive(u64 aruid) const { | ||
| 180 | return aruid == 0 || aruid == active_vibration_aruid; | ||
| 181 | } | ||
| 182 | |||
| 183 | u64 AppletResource::GetIndexFromAruid(u64 aruid) { | ||
| 184 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 185 | if (registration_list.flag[i] == RegistrationStatus::Initialized && | ||
| 186 | registration_list.aruid[i] == aruid) { | ||
| 187 | return i; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | return AruidIndexMax; | ||
| 191 | } | ||
| 192 | |||
| 193 | Result AppletResource::DestroySevenSixAxisTransferMemory() { | ||
| 194 | // TODO | ||
| 195 | return ResultSuccess; | ||
| 196 | } | ||
| 197 | |||
| 198 | void AppletResource::EnableInput(u64 aruid, bool is_enabled) { | ||
| 199 | const u64 index = GetIndexFromAruid(aruid); | ||
| 200 | if (index >= AruidIndexMax) { | ||
| 201 | return; | ||
| 202 | } | ||
| 203 | |||
| 204 | data[index].flag.enable_pad_input.Assign(is_enabled); | ||
| 205 | data[index].flag.enable_touchscreen.Assign(is_enabled); | ||
| 206 | } | ||
| 207 | |||
| 208 | void AppletResource::EnableSixAxisSensor(u64 aruid, bool is_enabled) { | ||
| 209 | const u64 index = GetIndexFromAruid(aruid); | ||
| 210 | if (index >= AruidIndexMax) { | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | |||
| 214 | data[index].flag.enable_six_axis_sensor.Assign(is_enabled); | ||
| 215 | } | ||
| 216 | |||
| 217 | void AppletResource::EnablePadInput(u64 aruid, bool is_enabled) { | ||
| 218 | const u64 index = GetIndexFromAruid(aruid); | ||
| 219 | if (index >= AruidIndexMax) { | ||
| 220 | return; | ||
| 221 | } | ||
| 222 | |||
| 223 | data[index].flag.enable_pad_input.Assign(is_enabled); | ||
| 224 | } | ||
| 225 | |||
| 226 | void AppletResource::EnableTouchScreen(u64 aruid, bool is_enabled) { | ||
| 227 | const u64 index = GetIndexFromAruid(aruid); | ||
| 228 | if (index >= AruidIndexMax) { | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | |||
| 232 | data[index].flag.enable_touchscreen.Assign(is_enabled); | ||
| 233 | } | ||
| 234 | |||
| 235 | void AppletResource::SetIsPalmaConnectable(u64 aruid, bool is_connectable) { | ||
| 236 | const u64 index = GetIndexFromAruid(aruid); | ||
| 237 | if (index >= AruidIndexMax) { | ||
| 238 | return; | ||
| 239 | } | ||
| 240 | |||
| 241 | data[index].flag.is_palma_connectable.Assign(is_connectable); | ||
| 242 | } | ||
| 243 | |||
| 244 | void AppletResource::EnablePalmaBoostMode(u64 aruid, bool is_enabled) { | ||
| 245 | const u64 index = GetIndexFromAruid(aruid); | ||
| 246 | if (index >= AruidIndexMax) { | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | |||
| 250 | data[index].flag.enable_palma_boost_mode.Assign(is_enabled); | ||
| 251 | } | ||
| 252 | |||
| 253 | Result AppletResource::RegisterCoreAppletResource() { | ||
| 254 | if (ref_counter == std::numeric_limits<s32>::max() - 1) { | ||
| 255 | return ResultAppletResourceOverflow; | ||
| 256 | } | ||
| 257 | if (ref_counter == 0) { | ||
| 258 | const u64 index = GetIndexFromAruid(0); | ||
| 259 | if (index < AruidIndexMax) { | ||
| 260 | return ResultAruidAlreadyRegistered; | ||
| 261 | } | ||
| 262 | |||
| 263 | std::size_t data_index = AruidIndexMax; | ||
| 264 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 265 | if (!data[i].flag.is_initialized) { | ||
| 266 | data_index = i; | ||
| 267 | break; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | if (data_index == AruidIndexMax) { | ||
| 272 | return ResultAruidNoAvailableEntries; | ||
| 273 | } | ||
| 274 | |||
| 275 | AruidData& aruid_data = data[data_index]; | ||
| 276 | |||
| 277 | aruid_data.aruid = 0; | ||
| 278 | aruid_data.flag.is_initialized.Assign(true); | ||
| 279 | aruid_data.flag.enable_pad_input.Assign(true); | ||
| 280 | aruid_data.flag.enable_six_axis_sensor.Assign(true); | ||
| 281 | aruid_data.flag.bit_18.Assign(true); | ||
| 282 | aruid_data.flag.enable_touchscreen.Assign(true); | ||
| 283 | |||
| 284 | data_index = AruidIndexMax; | ||
| 285 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 286 | if (registration_list.flag[i] == RegistrationStatus::Initialized) { | ||
| 287 | if (registration_list.aruid[i] != 0) { | ||
| 288 | continue; | ||
| 289 | } | ||
| 290 | data_index = i; | ||
| 291 | break; | ||
| 292 | } | ||
| 293 | if (registration_list.flag[i] == RegistrationStatus::None) { | ||
| 294 | data_index = i; | ||
| 295 | break; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | Result result = ResultSuccess; | ||
| 300 | |||
| 301 | if (data_index == AruidIndexMax) { | ||
| 302 | result = CreateAppletResource(0); | ||
| 303 | } else { | ||
| 304 | registration_list.flag[data_index] = RegistrationStatus::Initialized; | ||
| 305 | registration_list.aruid[data_index] = 0; | ||
| 306 | } | ||
| 307 | |||
| 308 | if (result.IsError()) { | ||
| 309 | UnregisterAppletResourceUserId(0); | ||
| 310 | return result; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | ref_counter++; | ||
| 314 | return ResultSuccess; | ||
| 315 | } | ||
| 316 | |||
| 317 | Result AppletResource::UnregisterCoreAppletResource() { | ||
| 318 | if (ref_counter == 0) { | ||
| 319 | return ResultAppletResourceNotInitialized; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (--ref_counter == 0) { | ||
| 323 | UnregisterAppletResourceUserId(0); | ||
| 324 | } | ||
| 325 | |||
| 326 | return ResultSuccess; | ||
| 327 | } | ||
| 328 | |||
| 329 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/applet_resource.h b/src/hid_core/resources/applet_resource.h new file mode 100644 index 000000000..f3f32bac1 --- /dev/null +++ b/src/hid_core/resources/applet_resource.h | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <mutex> | ||
| 8 | |||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | #include "hid_core/resources/shared_memory_holder.h" | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Kernel { | ||
| 19 | class KSharedMemory; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace Service::HID { | ||
| 23 | struct SharedMemoryFormat; | ||
| 24 | class AppletResource; | ||
| 25 | class NPadResource; | ||
| 26 | |||
| 27 | static constexpr std::size_t AruidIndexMax = 0x20; | ||
| 28 | static constexpr u64 SystemAruid = 0; | ||
| 29 | |||
| 30 | enum class RegistrationStatus : u32 { | ||
| 31 | None, | ||
| 32 | Initialized, | ||
| 33 | PendingDelete, | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct DataStatusFlag { | ||
| 37 | union { | ||
| 38 | u32 raw{}; | ||
| 39 | |||
| 40 | BitField<0, 1, u32> is_initialized; | ||
| 41 | BitField<1, 1, u32> is_assigned; | ||
| 42 | BitField<16, 1, u32> enable_pad_input; | ||
| 43 | BitField<17, 1, u32> enable_six_axis_sensor; | ||
| 44 | BitField<18, 1, u32> bit_18; | ||
| 45 | BitField<19, 1, u32> is_palma_connectable; | ||
| 46 | BitField<20, 1, u32> enable_palma_boost_mode; | ||
| 47 | BitField<21, 1, u32> enable_touchscreen; | ||
| 48 | }; | ||
| 49 | }; | ||
| 50 | |||
| 51 | struct AruidRegisterList { | ||
| 52 | std::array<RegistrationStatus, AruidIndexMax> flag{}; | ||
| 53 | std::array<u64, AruidIndexMax> aruid{}; | ||
| 54 | }; | ||
| 55 | static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size"); | ||
| 56 | |||
| 57 | struct AruidData { | ||
| 58 | DataStatusFlag flag{}; | ||
| 59 | u64 aruid{}; | ||
| 60 | SharedMemoryFormat* shared_memory_format{nullptr}; | ||
| 61 | }; | ||
| 62 | |||
| 63 | struct HandheldConfig { | ||
| 64 | bool is_handheld_hid_enabled; | ||
| 65 | bool is_force_handheld; | ||
| 66 | bool is_joycon_rail_enabled; | ||
| 67 | bool is_force_handheld_style_vibration; | ||
| 68 | }; | ||
| 69 | static_assert(sizeof(HandheldConfig) == 0x4, "HandheldConfig is an invalid size"); | ||
| 70 | |||
| 71 | struct AppletResourceHolder { | ||
| 72 | std::shared_ptr<AppletResource> applet_resource{nullptr}; | ||
| 73 | std::recursive_mutex* shared_mutex{nullptr}; | ||
| 74 | NPadResource* shared_npad_resource{nullptr}; | ||
| 75 | std::shared_ptr<HandheldConfig> handheld_config{nullptr}; | ||
| 76 | long* handle_1; | ||
| 77 | }; | ||
| 78 | |||
| 79 | class AppletResource { | ||
| 80 | public: | ||
| 81 | explicit AppletResource(Core::System& system_); | ||
| 82 | ~AppletResource(); | ||
| 83 | |||
| 84 | Result CreateAppletResource(u64 aruid); | ||
| 85 | |||
| 86 | Result RegisterAppletResourceUserId(u64 aruid, bool enable_input); | ||
| 87 | void UnregisterAppletResourceUserId(u64 aruid); | ||
| 88 | |||
| 89 | void FreeAppletResourceId(u64 aruid); | ||
| 90 | |||
| 91 | u64 GetActiveAruid(); | ||
| 92 | Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid); | ||
| 93 | Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid); | ||
| 94 | AruidData* GetAruidData(u64 aruid); | ||
| 95 | AruidData* GetAruidDataByIndex(std::size_t aruid_index); | ||
| 96 | |||
| 97 | bool IsVibrationAruidActive(u64 aruid) const; | ||
| 98 | |||
| 99 | u64 GetIndexFromAruid(u64 aruid); | ||
| 100 | |||
| 101 | Result DestroySevenSixAxisTransferMemory(); | ||
| 102 | |||
| 103 | void EnableInput(u64 aruid, bool is_enabled); | ||
| 104 | void EnableSixAxisSensor(u64 aruid, bool is_enabled); | ||
| 105 | void EnablePadInput(u64 aruid, bool is_enabled); | ||
| 106 | void EnableTouchScreen(u64 aruid, bool is_enabled); | ||
| 107 | void SetIsPalmaConnectable(u64 aruid, bool is_connectable); | ||
| 108 | void EnablePalmaBoostMode(u64 aruid, bool is_enabled); | ||
| 109 | |||
| 110 | Result RegisterCoreAppletResource(); | ||
| 111 | Result UnregisterCoreAppletResource(); | ||
| 112 | |||
| 113 | private: | ||
| 114 | u64 active_aruid{}; | ||
| 115 | AruidRegisterList registration_list{}; | ||
| 116 | std::array<AruidData, AruidIndexMax> data{}; | ||
| 117 | std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{}; | ||
| 118 | s32 ref_counter{}; | ||
| 119 | u64 active_vibration_aruid; | ||
| 120 | |||
| 121 | Core::System& system; | ||
| 122 | }; | ||
| 123 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/controller_base.cpp b/src/hid_core/resources/controller_base.cpp new file mode 100644 index 000000000..df5f5c884 --- /dev/null +++ b/src/hid_core/resources/controller_base.cpp | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "hid_core/resources/controller_base.h" | ||
| 5 | |||
| 6 | namespace Service::HID { | ||
| 7 | |||
| 8 | ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} | ||
| 9 | ControllerBase::~ControllerBase() = default; | ||
| 10 | |||
| 11 | Result ControllerBase::Activate() { | ||
| 12 | if (is_activated) { | ||
| 13 | return ResultSuccess; | ||
| 14 | } | ||
| 15 | is_activated = true; | ||
| 16 | OnInit(); | ||
| 17 | return ResultSuccess; | ||
| 18 | } | ||
| 19 | |||
| 20 | Result ControllerBase::Activate(u64 aruid) { | ||
| 21 | return Activate(); | ||
| 22 | } | ||
| 23 | |||
| 24 | void ControllerBase::DeactivateController() { | ||
| 25 | if (is_activated) { | ||
| 26 | OnRelease(); | ||
| 27 | } | ||
| 28 | is_activated = false; | ||
| 29 | } | ||
| 30 | |||
| 31 | bool ControllerBase::IsControllerActivated() const { | ||
| 32 | return is_activated; | ||
| 33 | } | ||
| 34 | |||
| 35 | void ControllerBase::SetAppletResource(std::shared_ptr<AppletResource> resource, | ||
| 36 | std::recursive_mutex* resource_mutex) { | ||
| 37 | applet_resource = resource; | ||
| 38 | shared_mutex = resource_mutex; | ||
| 39 | } | ||
| 40 | |||
| 41 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/controller_base.h b/src/hid_core/resources/controller_base.h new file mode 100644 index 000000000..e61bc6376 --- /dev/null +++ b/src/hid_core/resources/controller_base.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/result.h" | ||
| 10 | #include "hid_core/resources/applet_resource.h" | ||
| 11 | |||
| 12 | namespace Core::Timing { | ||
| 13 | class CoreTiming; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Core::HID { | ||
| 17 | class HIDCore; | ||
| 18 | } // namespace Core::HID | ||
| 19 | |||
| 20 | namespace Service::HID { | ||
| 21 | class ControllerBase { | ||
| 22 | public: | ||
| 23 | explicit ControllerBase(Core::HID::HIDCore& hid_core_); | ||
| 24 | virtual ~ControllerBase(); | ||
| 25 | |||
| 26 | // Called when the controller is initialized | ||
| 27 | virtual void OnInit() = 0; | ||
| 28 | |||
| 29 | // When the controller is released | ||
| 30 | virtual void OnRelease() = 0; | ||
| 31 | |||
| 32 | // When the controller is requesting an update for the shared memory | ||
| 33 | virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0; | ||
| 34 | |||
| 35 | // When the controller is requesting a motion update for the shared memory | ||
| 36 | virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} | ||
| 37 | |||
| 38 | Result Activate(); | ||
| 39 | Result Activate(u64 aruid); | ||
| 40 | |||
| 41 | void DeactivateController(); | ||
| 42 | |||
| 43 | bool IsControllerActivated() const; | ||
| 44 | |||
| 45 | void SetAppletResource(std::shared_ptr<AppletResource> resource, | ||
| 46 | std::recursive_mutex* resource_mutex); | ||
| 47 | |||
| 48 | protected: | ||
| 49 | bool is_activated{false}; | ||
| 50 | std::shared_ptr<AppletResource> applet_resource{nullptr}; | ||
| 51 | std::recursive_mutex* shared_mutex{nullptr}; | ||
| 52 | |||
| 53 | Core::HID::HIDCore& hid_core; | ||
| 54 | }; | ||
| 55 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/debug_pad/debug_pad.cpp b/src/hid_core/resources/debug_pad/debug_pad.cpp new file mode 100644 index 000000000..1102dad6c --- /dev/null +++ b/src/hid_core/resources/debug_pad/debug_pad.cpp | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/settings.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "hid_core/frontend/emulated_controller.h" | ||
| 7 | #include "hid_core/hid_core.h" | ||
| 8 | #include "hid_core/hid_types.h" | ||
| 9 | #include "hid_core/resources/applet_resource.h" | ||
| 10 | #include "hid_core/resources/debug_pad/debug_pad.h" | ||
| 11 | #include "hid_core/resources/shared_memory_format.h" | ||
| 12 | |||
| 13 | namespace Service::HID { | ||
| 14 | |||
| 15 | DebugPad::DebugPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { | ||
| 16 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); | ||
| 17 | } | ||
| 18 | |||
| 19 | DebugPad::~DebugPad() = default; | ||
| 20 | |||
| 21 | void DebugPad::OnInit() {} | ||
| 22 | |||
| 23 | void DebugPad::OnRelease() {} | ||
| 24 | |||
| 25 | void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 26 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 27 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 28 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 29 | |||
| 30 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | |||
| 34 | DebugPadSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_pad; | ||
| 35 | |||
| 36 | if (!IsControllerActivated()) { | ||
| 37 | shared_memory.debug_pad_lifo.buffer_count = 0; | ||
| 38 | shared_memory.debug_pad_lifo.buffer_tail = 0; | ||
| 39 | return; | ||
| 40 | } | ||
| 41 | |||
| 42 | const auto& last_entry = shared_memory.debug_pad_lifo.ReadCurrentEntry().state; | ||
| 43 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 44 | |||
| 45 | if (Settings::values.debug_pad_enabled) { | ||
| 46 | next_state.attribute.connected.Assign(1); | ||
| 47 | |||
| 48 | const auto& button_state = controller->GetDebugPadButtons(); | ||
| 49 | const auto& stick_state = controller->GetSticks(); | ||
| 50 | |||
| 51 | next_state.pad_state = button_state; | ||
| 52 | next_state.l_stick = stick_state.left; | ||
| 53 | next_state.r_stick = stick_state.right; | ||
| 54 | } | ||
| 55 | |||
| 56 | shared_memory.debug_pad_lifo.WriteNextEntry(next_state); | ||
| 57 | } | ||
| 58 | |||
| 59 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/debug_pad/debug_pad.h b/src/hid_core/resources/debug_pad/debug_pad.h new file mode 100644 index 000000000..73c3d4421 --- /dev/null +++ b/src/hid_core/resources/debug_pad/debug_pad.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/resources/controller_base.h" | ||
| 7 | #include "hid_core/resources/debug_pad/debug_pad_types.h" | ||
| 8 | |||
| 9 | namespace Core::HID { | ||
| 10 | class HIDCore; | ||
| 11 | class EmulatedController; | ||
| 12 | } // namespace Core::HID | ||
| 13 | |||
| 14 | namespace Core::Timing { | ||
| 15 | class CoreTiming; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Service::HID { | ||
| 19 | class DebugPad final : public ControllerBase { | ||
| 20 | public: | ||
| 21 | explicit DebugPad(Core::HID::HIDCore& hid_core_); | ||
| 22 | ~DebugPad() override; | ||
| 23 | |||
| 24 | // Called when the controller is initialized | ||
| 25 | void OnInit() override; | ||
| 26 | |||
| 27 | // When the controller is released | ||
| 28 | void OnRelease() override; | ||
| 29 | |||
| 30 | // When the controller is requesting an update for the shared memory | ||
| 31 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 32 | |||
| 33 | private: | ||
| 34 | DebugPadState next_state{}; | ||
| 35 | Core::HID::EmulatedController* controller = nullptr; | ||
| 36 | }; | ||
| 37 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/debug_pad/debug_pad_types.h b/src/hid_core/resources/debug_pad/debug_pad_types.h new file mode 100644 index 000000000..8b5eb108e --- /dev/null +++ b/src/hid_core/resources/debug_pad/debug_pad_types.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/bit_field.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "hid_core/hid_types.h" | ||
| 9 | |||
| 10 | namespace Service::HID { | ||
| 11 | |||
| 12 | // This is nn::hid::DebugPadAttribute | ||
| 13 | struct DebugPadAttribute { | ||
| 14 | union { | ||
| 15 | u32 raw{}; | ||
| 16 | BitField<0, 1, u32> connected; | ||
| 17 | }; | ||
| 18 | }; | ||
| 19 | static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size"); | ||
| 20 | |||
| 21 | // This is nn::hid::DebugPadState | ||
| 22 | struct DebugPadState { | ||
| 23 | s64 sampling_number{}; | ||
| 24 | DebugPadAttribute attribute{}; | ||
| 25 | Core::HID::DebugPadButton pad_state{}; | ||
| 26 | Core::HID::AnalogStickState r_stick{}; | ||
| 27 | Core::HID::AnalogStickState l_stick{}; | ||
| 28 | }; | ||
| 29 | static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state"); | ||
| 30 | |||
| 31 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/digitizer/digitizer.cpp b/src/hid_core/resources/digitizer/digitizer.cpp new file mode 100644 index 000000000..cd72fd6e5 --- /dev/null +++ b/src/hid_core/resources/digitizer/digitizer.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "hid_core/resources/applet_resource.h" | ||
| 6 | #include "hid_core/resources/digitizer/digitizer.h" | ||
| 7 | #include "hid_core/resources/shared_memory_format.h" | ||
| 8 | |||
| 9 | namespace Service::HID { | ||
| 10 | |||
| 11 | Digitizer::Digitizer(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} | ||
| 12 | |||
| 13 | Digitizer::~Digitizer() = default; | ||
| 14 | |||
| 15 | void Digitizer::OnInit() {} | ||
| 16 | |||
| 17 | void Digitizer::OnRelease() {} | ||
| 18 | |||
| 19 | void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 20 | if (!smart_update) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | |||
| 24 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 25 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 26 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 27 | |||
| 28 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | |||
| 32 | auto& header = data->shared_memory_format->digitizer.header; | ||
| 33 | header.timestamp = core_timing.GetGlobalTimeNs().count(); | ||
| 34 | header.total_entry_count = 17; | ||
| 35 | header.entry_count = 0; | ||
| 36 | header.last_entry_index = 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/digitizer/digitizer.h b/src/hid_core/resources/digitizer/digitizer.h new file mode 100644 index 000000000..e031a16b0 --- /dev/null +++ b/src/hid_core/resources/digitizer/digitizer.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/resources/controller_base.h" | ||
| 7 | |||
| 8 | namespace Service::HID { | ||
| 9 | |||
| 10 | class Digitizer final : public ControllerBase { | ||
| 11 | public: | ||
| 12 | explicit Digitizer(Core::HID::HIDCore& hid_core_); | ||
| 13 | ~Digitizer() override; | ||
| 14 | |||
| 15 | // Called when the controller is initialized | ||
| 16 | void OnInit() override; | ||
| 17 | |||
| 18 | // When the controller is released | ||
| 19 | void OnRelease() override; | ||
| 20 | |||
| 21 | // When the controller is requesting an update for the shared memory | ||
| 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 23 | |||
| 24 | private: | ||
| 25 | bool smart_update{}; | ||
| 26 | }; | ||
| 27 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/hid_firmware_settings.cpp b/src/hid_core/resources/hid_firmware_settings.cpp new file mode 100644 index 000000000..e76b3a016 --- /dev/null +++ b/src/hid_core/resources/hid_firmware_settings.cpp | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "hid_core/resources/hid_firmware_settings.h" | ||
| 5 | |||
| 6 | namespace Service::HID { | ||
| 7 | |||
| 8 | HidFirmwareSettings::HidFirmwareSettings() { | ||
| 9 | LoadSettings(true); | ||
| 10 | } | ||
| 11 | |||
| 12 | void HidFirmwareSettings::Reload() { | ||
| 13 | LoadSettings(true); | ||
| 14 | } | ||
| 15 | |||
| 16 | void HidFirmwareSettings::LoadSettings(bool reload_config) { | ||
| 17 | if (is_initalized && !reload_config) { | ||
| 18 | return; | ||
| 19 | } | ||
| 20 | |||
| 21 | // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values | ||
| 22 | |||
| 23 | is_debug_pad_enabled = true; | ||
| 24 | is_device_managed = true; | ||
| 25 | is_touch_i2c_managed = is_device_managed; | ||
| 26 | is_future_devices_emulated = false; | ||
| 27 | is_mcu_hardware_error_emulated = false; | ||
| 28 | is_rail_enabled = true; | ||
| 29 | is_firmware_update_failure_emulated = false; | ||
| 30 | is_firmware_update_failure = {}; | ||
| 31 | is_ble_disabled = false; | ||
| 32 | is_dscale_disabled = false; | ||
| 33 | is_handheld_forced = true; | ||
| 34 | features_per_id_disabled = {}; | ||
| 35 | is_touch_firmware_auto_update_disabled = false; | ||
| 36 | is_initalized = true; | ||
| 37 | } | ||
| 38 | |||
| 39 | bool HidFirmwareSettings::IsDebugPadEnabled() { | ||
| 40 | LoadSettings(false); | ||
| 41 | return is_debug_pad_enabled; | ||
| 42 | } | ||
| 43 | |||
| 44 | bool HidFirmwareSettings::IsDeviceManaged() { | ||
| 45 | LoadSettings(false); | ||
| 46 | return is_device_managed; | ||
| 47 | } | ||
| 48 | |||
| 49 | bool HidFirmwareSettings::IsEmulateFutureDevice() { | ||
| 50 | LoadSettings(false); | ||
| 51 | return is_future_devices_emulated; | ||
| 52 | } | ||
| 53 | |||
| 54 | bool HidFirmwareSettings::IsTouchI2cManaged() { | ||
| 55 | LoadSettings(false); | ||
| 56 | return is_touch_i2c_managed; | ||
| 57 | } | ||
| 58 | |||
| 59 | bool HidFirmwareSettings::IsHandheldForced() { | ||
| 60 | LoadSettings(false); | ||
| 61 | return is_handheld_forced; | ||
| 62 | } | ||
| 63 | |||
| 64 | bool HidFirmwareSettings::IsRailEnabled() { | ||
| 65 | LoadSettings(false); | ||
| 66 | return is_rail_enabled; | ||
| 67 | } | ||
| 68 | |||
| 69 | bool HidFirmwareSettings::IsHardwareErrorEmulated() { | ||
| 70 | LoadSettings(false); | ||
| 71 | return is_mcu_hardware_error_emulated; | ||
| 72 | } | ||
| 73 | |||
| 74 | bool HidFirmwareSettings::IsBleDisabled() { | ||
| 75 | LoadSettings(false); | ||
| 76 | return is_ble_disabled; | ||
| 77 | } | ||
| 78 | |||
| 79 | bool HidFirmwareSettings::IsDscaleDisabled() { | ||
| 80 | LoadSettings(false); | ||
| 81 | return is_dscale_disabled; | ||
| 82 | } | ||
| 83 | |||
| 84 | bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() { | ||
| 85 | LoadSettings(false); | ||
| 86 | return is_touch_firmware_auto_update_disabled; | ||
| 87 | } | ||
| 88 | |||
| 89 | HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() { | ||
| 90 | LoadSettings(false); | ||
| 91 | return is_firmware_update_failure; | ||
| 92 | } | ||
| 93 | |||
| 94 | HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() { | ||
| 95 | LoadSettings(false); | ||
| 96 | return features_per_id_disabled; | ||
| 97 | } | ||
| 98 | |||
| 99 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/hid_firmware_settings.h b/src/hid_core/resources/hid_firmware_settings.h new file mode 100644 index 000000000..6c10c440b --- /dev/null +++ b/src/hid_core/resources/hid_firmware_settings.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace Service::HID { | ||
| 9 | |||
| 10 | /// Loads firmware config from nn::settings::fwdbg | ||
| 11 | class HidFirmwareSettings { | ||
| 12 | public: | ||
| 13 | using FirmwareSetting = std::array<u8, 4>; | ||
| 14 | using FeaturesPerId = std::array<bool, 0xA8>; | ||
| 15 | |||
| 16 | HidFirmwareSettings(); | ||
| 17 | |||
| 18 | void Reload(); | ||
| 19 | void LoadSettings(bool reload_config); | ||
| 20 | |||
| 21 | bool IsDebugPadEnabled(); | ||
| 22 | bool IsDeviceManaged(); | ||
| 23 | bool IsEmulateFutureDevice(); | ||
| 24 | bool IsTouchI2cManaged(); | ||
| 25 | bool IsHandheldForced(); | ||
| 26 | bool IsRailEnabled(); | ||
| 27 | bool IsHardwareErrorEmulated(); | ||
| 28 | bool IsBleDisabled(); | ||
| 29 | bool IsDscaleDisabled(); | ||
| 30 | bool IsTouchAutoUpdateDisabled(); | ||
| 31 | |||
| 32 | FirmwareSetting GetFirmwareUpdateFailure(); | ||
| 33 | FeaturesPerId FeaturesDisabledPerId(); | ||
| 34 | |||
| 35 | private: | ||
| 36 | bool is_initalized{}; | ||
| 37 | |||
| 38 | // Debug settings | ||
| 39 | bool is_debug_pad_enabled{}; | ||
| 40 | bool is_device_managed{}; | ||
| 41 | bool is_touch_i2c_managed{}; | ||
| 42 | bool is_future_devices_emulated{}; | ||
| 43 | bool is_mcu_hardware_error_emulated{}; | ||
| 44 | bool is_rail_enabled{}; | ||
| 45 | bool is_firmware_update_failure_emulated{}; | ||
| 46 | bool is_ble_disabled{}; | ||
| 47 | bool is_dscale_disabled{}; | ||
| 48 | bool is_handheld_forced{}; | ||
| 49 | bool is_touch_firmware_auto_update_disabled{}; | ||
| 50 | FirmwareSetting is_firmware_update_failure{}; | ||
| 51 | FeaturesPerId features_per_id_disabled{}; | ||
| 52 | }; | ||
| 53 | |||
| 54 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/irs_ring_lifo.h b/src/hid_core/resources/irs_ring_lifo.h new file mode 100644 index 000000000..255d1d296 --- /dev/null +++ b/src/hid_core/resources/irs_ring_lifo.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace Service::IRS { | ||
| 11 | |||
| 12 | template <typename State, std::size_t max_buffer_size> | ||
| 13 | struct Lifo { | ||
| 14 | s64 sampling_number{}; | ||
| 15 | s64 buffer_count{}; | ||
| 16 | std::array<State, max_buffer_size> entries{}; | ||
| 17 | |||
| 18 | const State& ReadCurrentEntry() const { | ||
| 19 | return entries[GetBufferTail()]; | ||
| 20 | } | ||
| 21 | |||
| 22 | const State& ReadPreviousEntry() const { | ||
| 23 | return entries[GetPreviousEntryIndex()]; | ||
| 24 | } | ||
| 25 | |||
| 26 | s64 GetBufferTail() const { | ||
| 27 | return sampling_number % max_buffer_size; | ||
| 28 | } | ||
| 29 | |||
| 30 | std::size_t GetPreviousEntryIndex() const { | ||
| 31 | return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size); | ||
| 32 | } | ||
| 33 | |||
| 34 | std::size_t GetNextEntryIndex() const { | ||
| 35 | return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size); | ||
| 36 | } | ||
| 37 | |||
| 38 | void WriteNextEntry(const State& new_state) { | ||
| 39 | if (buffer_count < static_cast<s64>(max_buffer_size)) { | ||
| 40 | buffer_count++; | ||
| 41 | } | ||
| 42 | sampling_number++; | ||
| 43 | entries[GetBufferTail()] = new_state; | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // namespace Service::IRS | ||
diff --git a/src/hid_core/resources/keyboard/keyboard.cpp b/src/hid_core/resources/keyboard/keyboard.cpp new file mode 100644 index 000000000..340e8a65c --- /dev/null +++ b/src/hid_core/resources/keyboard/keyboard.cpp | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/settings.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "hid_core/frontend/emulated_devices.h" | ||
| 7 | #include "hid_core/hid_core.h" | ||
| 8 | #include "hid_core/resources/applet_resource.h" | ||
| 9 | #include "hid_core/resources/keyboard/keyboard.h" | ||
| 10 | #include "hid_core/resources/shared_memory_format.h" | ||
| 11 | |||
| 12 | namespace Service::HID { | ||
| 13 | |||
| 14 | Keyboard::Keyboard(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { | ||
| 15 | emulated_devices = hid_core.GetEmulatedDevices(); | ||
| 16 | } | ||
| 17 | |||
| 18 | Keyboard::~Keyboard() = default; | ||
| 19 | |||
| 20 | void Keyboard::OnInit() {} | ||
| 21 | |||
| 22 | void Keyboard::OnRelease() {} | ||
| 23 | |||
| 24 | void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 25 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 26 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 27 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 28 | |||
| 29 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | |||
| 33 | KeyboardSharedMemoryFormat& shared_memory = data->shared_memory_format->keyboard; | ||
| 34 | |||
| 35 | if (!IsControllerActivated()) { | ||
| 36 | shared_memory.keyboard_lifo.buffer_count = 0; | ||
| 37 | shared_memory.keyboard_lifo.buffer_tail = 0; | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | |||
| 41 | const auto& last_entry = shared_memory.keyboard_lifo.ReadCurrentEntry().state; | ||
| 42 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 43 | |||
| 44 | if (Settings::values.keyboard_enabled) { | ||
| 45 | const auto& keyboard_state = emulated_devices->GetKeyboard(); | ||
| 46 | const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier(); | ||
| 47 | |||
| 48 | next_state.key = keyboard_state; | ||
| 49 | next_state.modifier = keyboard_modifier_state; | ||
| 50 | next_state.attribute.is_connected.Assign(1); | ||
| 51 | } | ||
| 52 | |||
| 53 | shared_memory.keyboard_lifo.WriteNextEntry(next_state); | ||
| 54 | } | ||
| 55 | |||
| 56 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/keyboard/keyboard.h b/src/hid_core/resources/keyboard/keyboard.h new file mode 100644 index 000000000..4bcc1c1b2 --- /dev/null +++ b/src/hid_core/resources/keyboard/keyboard.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/resources/controller_base.h" | ||
| 7 | #include "hid_core/resources/keyboard/keyboard_types.h" | ||
| 8 | |||
| 9 | namespace Core::HID { | ||
| 10 | class HIDCore; | ||
| 11 | class EmulatedDevices; | ||
| 12 | } // namespace Core::HID | ||
| 13 | |||
| 14 | namespace Service::HID { | ||
| 15 | class Keyboard final : public ControllerBase { | ||
| 16 | public: | ||
| 17 | explicit Keyboard(Core::HID::HIDCore& hid_core_); | ||
| 18 | ~Keyboard() override; | ||
| 19 | |||
| 20 | // Called when the controller is initialized | ||
| 21 | void OnInit() override; | ||
| 22 | |||
| 23 | // When the controller is released | ||
| 24 | void OnRelease() override; | ||
| 25 | |||
| 26 | // When the controller is requesting an update for the shared memory | ||
| 27 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | KeyboardState next_state{}; | ||
| 31 | Core::HID::EmulatedDevices* emulated_devices = nullptr; | ||
| 32 | }; | ||
| 33 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/keyboard/keyboard_types.h b/src/hid_core/resources/keyboard/keyboard_types.h new file mode 100644 index 000000000..4d7ff2f0a --- /dev/null +++ b/src/hid_core/resources/keyboard/keyboard_types.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "hid_core/hid_types.h" | ||
| 8 | |||
| 9 | namespace Service::HID { | ||
| 10 | |||
| 11 | // This is nn::hid::detail::KeyboardState | ||
| 12 | struct KeyboardState { | ||
| 13 | s64 sampling_number{}; | ||
| 14 | Core::HID::KeyboardModifier modifier{}; | ||
| 15 | Core::HID::KeyboardAttribute attribute{}; | ||
| 16 | Core::HID::KeyboardKey key{}; | ||
| 17 | }; | ||
| 18 | static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size"); | ||
| 19 | |||
| 20 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/mouse/debug_mouse.cpp b/src/hid_core/resources/mouse/debug_mouse.cpp new file mode 100644 index 000000000..5f6f6e8e1 --- /dev/null +++ b/src/hid_core/resources/mouse/debug_mouse.cpp | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "core/frontend/emu_window.h" | ||
| 6 | #include "hid_core/frontend/emulated_devices.h" | ||
| 7 | #include "hid_core/hid_core.h" | ||
| 8 | #include "hid_core/resources/applet_resource.h" | ||
| 9 | #include "hid_core/resources/mouse/debug_mouse.h" | ||
| 10 | #include "hid_core/resources/shared_memory_format.h" | ||
| 11 | |||
| 12 | namespace Service::HID { | ||
| 13 | |||
| 14 | DebugMouse::DebugMouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { | ||
| 15 | emulated_devices = hid_core.GetEmulatedDevices(); | ||
| 16 | } | ||
| 17 | |||
| 18 | DebugMouse::~DebugMouse() = default; | ||
| 19 | |||
| 20 | void DebugMouse::OnInit() {} | ||
| 21 | void DebugMouse::OnRelease() {} | ||
| 22 | |||
| 23 | void DebugMouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 24 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 25 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 26 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 27 | |||
| 28 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | |||
| 32 | MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_mouse; | ||
| 33 | |||
| 34 | if (!IsControllerActivated()) { | ||
| 35 | shared_memory.mouse_lifo.buffer_count = 0; | ||
| 36 | shared_memory.mouse_lifo.buffer_tail = 0; | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | |||
| 40 | next_state = {}; | ||
| 41 | |||
| 42 | const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state; | ||
| 43 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 44 | |||
| 45 | if (Settings::values.mouse_enabled) { | ||
| 46 | const auto& mouse_button_state = emulated_devices->GetMouseButtons(); | ||
| 47 | const auto& mouse_position_state = emulated_devices->GetMousePosition(); | ||
| 48 | const auto& mouse_wheel_state = emulated_devices->GetMouseWheel(); | ||
| 49 | next_state.attribute.is_connected.Assign(1); | ||
| 50 | next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width); | ||
| 51 | next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height); | ||
| 52 | next_state.delta_x = next_state.x - last_entry.x; | ||
| 53 | next_state.delta_y = next_state.y - last_entry.y; | ||
| 54 | next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x; | ||
| 55 | next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y; | ||
| 56 | |||
| 57 | last_mouse_wheel_state = mouse_wheel_state; | ||
| 58 | next_state.button = mouse_button_state; | ||
| 59 | } | ||
| 60 | |||
| 61 | shared_memory.mouse_lifo.WriteNextEntry(next_state); | ||
| 62 | } | ||
| 63 | |||
| 64 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/mouse/debug_mouse.h b/src/hid_core/resources/mouse/debug_mouse.h new file mode 100644 index 000000000..006b53da6 --- /dev/null +++ b/src/hid_core/resources/mouse/debug_mouse.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/hid_types.h" | ||
| 7 | #include "hid_core/resources/controller_base.h" | ||
| 8 | |||
| 9 | namespace Core::HID { | ||
| 10 | class HIDCore; | ||
| 11 | class EmulatedDevices; | ||
| 12 | } // namespace Core::HID | ||
| 13 | |||
| 14 | namespace Service::HID { | ||
| 15 | class DebugMouse final : public ControllerBase { | ||
| 16 | public: | ||
| 17 | explicit DebugMouse(Core::HID::HIDCore& hid_core_); | ||
| 18 | ~DebugMouse() override; | ||
| 19 | |||
| 20 | // Called when the controller is initialized | ||
| 21 | void OnInit() override; | ||
| 22 | |||
| 23 | // When the controller is released | ||
| 24 | void OnRelease() override; | ||
| 25 | |||
| 26 | // When the controller is requesting an update for the shared memory | ||
| 27 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | Core::HID::MouseState next_state{}; | ||
| 31 | Core::HID::AnalogStickState last_mouse_wheel_state{}; | ||
| 32 | Core::HID::EmulatedDevices* emulated_devices = nullptr; | ||
| 33 | }; | ||
| 34 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/mouse/mouse.cpp b/src/hid_core/resources/mouse/mouse.cpp new file mode 100644 index 000000000..53a8938a1 --- /dev/null +++ b/src/hid_core/resources/mouse/mouse.cpp | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "core/frontend/emu_window.h" | ||
| 6 | #include "hid_core/frontend/emulated_devices.h" | ||
| 7 | #include "hid_core/hid_core.h" | ||
| 8 | #include "hid_core/resources/applet_resource.h" | ||
| 9 | #include "hid_core/resources/mouse/mouse.h" | ||
| 10 | #include "hid_core/resources/shared_memory_format.h" | ||
| 11 | |||
| 12 | namespace Service::HID { | ||
| 13 | |||
| 14 | Mouse::Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { | ||
| 15 | emulated_devices = hid_core.GetEmulatedDevices(); | ||
| 16 | } | ||
| 17 | |||
| 18 | Mouse::~Mouse() = default; | ||
| 19 | |||
| 20 | void Mouse::OnInit() {} | ||
| 21 | void Mouse::OnRelease() {} | ||
| 22 | |||
| 23 | void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 24 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 25 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 26 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 27 | |||
| 28 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | |||
| 32 | MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->mouse; | ||
| 33 | |||
| 34 | if (!IsControllerActivated()) { | ||
| 35 | shared_memory.mouse_lifo.buffer_count = 0; | ||
| 36 | shared_memory.mouse_lifo.buffer_tail = 0; | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | |||
| 40 | next_state = {}; | ||
| 41 | |||
| 42 | const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state; | ||
| 43 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 44 | |||
| 45 | if (Settings::values.mouse_enabled) { | ||
| 46 | const auto& mouse_button_state = emulated_devices->GetMouseButtons(); | ||
| 47 | const auto& mouse_position_state = emulated_devices->GetMousePosition(); | ||
| 48 | const auto& mouse_wheel_state = emulated_devices->GetMouseWheel(); | ||
| 49 | next_state.attribute.is_connected.Assign(1); | ||
| 50 | next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width); | ||
| 51 | next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height); | ||
| 52 | next_state.delta_x = next_state.x - last_entry.x; | ||
| 53 | next_state.delta_y = next_state.y - last_entry.y; | ||
| 54 | next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x; | ||
| 55 | next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y; | ||
| 56 | |||
| 57 | last_mouse_wheel_state = mouse_wheel_state; | ||
| 58 | next_state.button = mouse_button_state; | ||
| 59 | } | ||
| 60 | |||
| 61 | shared_memory.mouse_lifo.WriteNextEntry(next_state); | ||
| 62 | } | ||
| 63 | |||
| 64 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/mouse/mouse.h b/src/hid_core/resources/mouse/mouse.h new file mode 100644 index 000000000..e9ac6ad36 --- /dev/null +++ b/src/hid_core/resources/mouse/mouse.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/hid_types.h" | ||
| 7 | #include "hid_core/resources/controller_base.h" | ||
| 8 | |||
| 9 | namespace Core::HID { | ||
| 10 | class HIDCore; | ||
| 11 | class EmulatedDevices; | ||
| 12 | } // namespace Core::HID | ||
| 13 | |||
| 14 | namespace Service::HID { | ||
| 15 | class Mouse final : public ControllerBase { | ||
| 16 | public: | ||
| 17 | explicit Mouse(Core::HID::HIDCore& hid_core_); | ||
| 18 | ~Mouse() override; | ||
| 19 | |||
| 20 | // Called when the controller is initialized | ||
| 21 | void OnInit() override; | ||
| 22 | |||
| 23 | // When the controller is released | ||
| 24 | void OnRelease() override; | ||
| 25 | |||
| 26 | // When the controller is requesting an update for the shared memory | ||
| 27 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | Core::HID::MouseState next_state{}; | ||
| 31 | Core::HID::AnalogStickState last_mouse_wheel_state{}; | ||
| 32 | Core::HID::EmulatedDevices* emulated_devices = nullptr; | ||
| 33 | }; | ||
| 34 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/mouse/mouse_types.h b/src/hid_core/resources/mouse/mouse_types.h new file mode 100644 index 000000000..8bd6e167c --- /dev/null +++ b/src/hid_core/resources/mouse/mouse_types.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace Service::HID {} // namespace Service::HID | ||
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp new file mode 100644 index 000000000..e6c035628 --- /dev/null +++ b/src/hid_core/resources/npad/npad.cpp | |||
| @@ -0,0 +1,1342 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <array> | ||
| 6 | #include <chrono> | ||
| 7 | #include <cstring> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/settings.h" | ||
| 14 | #include "core/core_timing.h" | ||
| 15 | #include "core/hle/kernel/k_event.h" | ||
| 16 | #include "core/hle/kernel/k_readable_event.h" | ||
| 17 | #include "core/hle/service/kernel_helpers.h" | ||
| 18 | #include "hid_core/frontend/emulated_controller.h" | ||
| 19 | #include "hid_core/hid_core.h" | ||
| 20 | #include "hid_core/hid_result.h" | ||
| 21 | #include "hid_core/hid_util.h" | ||
| 22 | #include "hid_core/resources/applet_resource.h" | ||
| 23 | #include "hid_core/resources/npad/npad.h" | ||
| 24 | #include "hid_core/resources/shared_memory_format.h" | ||
| 25 | |||
| 26 | namespace Service::HID { | ||
| 27 | |||
| 28 | NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_) | ||
| 29 | : hid_core{hid_core_}, service_context{service_context_}, npad_resource{service_context} { | ||
| 30 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { | ||
| 31 | for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { | ||
| 32 | auto& controller = controller_data[aruid_index][i]; | ||
| 33 | controller.device = hid_core.GetEmulatedControllerByIndex(i); | ||
| 34 | controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = | ||
| 35 | Core::HID::DEFAULT_VIBRATION_VALUE; | ||
| 36 | controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex] | ||
| 37 | .latest_vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE; | ||
| 38 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 39 | .on_change = | ||
| 40 | [this, i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); }, | ||
| 41 | .is_npad_service = true, | ||
| 42 | }; | ||
| 43 | controller.callback_key = controller.device->SetCallback(engine_callback); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | NPad::~NPad() { | ||
| 49 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { | ||
| 50 | for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { | ||
| 51 | auto& controller = controller_data[aruid_index][i]; | ||
| 52 | controller.device->DeleteCallback(controller.callback_key); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | Result NPad::Activate() { | ||
| 58 | if (ref_counter == std::numeric_limits<s32>::max() - 1) { | ||
| 59 | return ResultNpadResourceOverflow; | ||
| 60 | } | ||
| 61 | |||
| 62 | if (ref_counter == 0) { | ||
| 63 | std::scoped_lock lock{mutex}; | ||
| 64 | |||
| 65 | // TODO: Activate handlers and AbstractedPad | ||
| 66 | } | ||
| 67 | |||
| 68 | ref_counter++; | ||
| 69 | return ResultSuccess; | ||
| 70 | } | ||
| 71 | |||
| 72 | Result NPad::Activate(u64 aruid) { | ||
| 73 | std::scoped_lock lock{mutex}; | ||
| 74 | std::scoped_lock shared_lock{*applet_resource_holder.shared_mutex}; | ||
| 75 | |||
| 76 | auto* data = applet_resource_holder.applet_resource->GetAruidData(aruid); | ||
| 77 | const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid); | ||
| 78 | |||
| 79 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 80 | return ResultSuccess; | ||
| 81 | } | ||
| 82 | |||
| 83 | for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { | ||
| 84 | auto& controller = controller_data[aruid_index][i]; | ||
| 85 | controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state; | ||
| 86 | } | ||
| 87 | |||
| 88 | // Prefill controller buffers | ||
| 89 | for (auto& controller : controller_data[aruid_index]) { | ||
| 90 | auto* npad = controller.shared_memory; | ||
| 91 | npad->fullkey_color = { | ||
| 92 | .attribute = ColorAttribute::NoController, | ||
| 93 | .fullkey = {}, | ||
| 94 | }; | ||
| 95 | npad->joycon_color = { | ||
| 96 | .attribute = ColorAttribute::NoController, | ||
| 97 | .left = {}, | ||
| 98 | .right = {}, | ||
| 99 | }; | ||
| 100 | // HW seems to initialize the first 19 entries | ||
| 101 | for (std::size_t i = 0; i < 19; ++i) { | ||
| 102 | WriteEmptyEntry(npad); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | return ResultSuccess; | ||
| 107 | } | ||
| 108 | |||
| 109 | Result NPad::ActivateNpadResource() { | ||
| 110 | return npad_resource.Activate(); | ||
| 111 | } | ||
| 112 | |||
| 113 | Result NPad::ActivateNpadResource(u64 aruid) { | ||
| 114 | return npad_resource.Activate(aruid); | ||
| 115 | } | ||
| 116 | |||
| 117 | void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { | ||
| 118 | if (type == Core::HID::ControllerTriggerType::All) { | ||
| 119 | ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); | ||
| 120 | ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); | ||
| 121 | return; | ||
| 122 | } | ||
| 123 | |||
| 124 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 125 | if (controller_idx >= controller_data[aruid_index].size()) { | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | |||
| 129 | auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 130 | |||
| 131 | if (!data->flag.is_assigned) { | ||
| 132 | continue; | ||
| 133 | } | ||
| 134 | |||
| 135 | auto& controller = controller_data[aruid_index][controller_idx]; | ||
| 136 | const auto is_connected = controller.device->IsConnected(); | ||
| 137 | const auto npad_type = controller.device->GetNpadStyleIndex(); | ||
| 138 | const auto npad_id = controller.device->GetNpadIdType(); | ||
| 139 | switch (type) { | ||
| 140 | case Core::HID::ControllerTriggerType::Connected: | ||
| 141 | case Core::HID::ControllerTriggerType::Disconnected: | ||
| 142 | if (is_connected == controller.is_connected) { | ||
| 143 | return; | ||
| 144 | } | ||
| 145 | UpdateControllerAt(data->aruid, npad_type, npad_id, is_connected); | ||
| 146 | break; | ||
| 147 | case Core::HID::ControllerTriggerType::Battery: { | ||
| 148 | if (!controller.device->IsConnected()) { | ||
| 149 | return; | ||
| 150 | } | ||
| 151 | auto* shared_memory = controller.shared_memory; | ||
| 152 | const auto& battery_level = controller.device->GetBattery(); | ||
| 153 | shared_memory->battery_level_dual = battery_level.dual.battery_level; | ||
| 154 | shared_memory->battery_level_left = battery_level.left.battery_level; | ||
| 155 | shared_memory->battery_level_right = battery_level.right.battery_level; | ||
| 156 | break; | ||
| 157 | } | ||
| 158 | default: | ||
| 159 | break; | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) { | ||
| 165 | auto& controller = GetControllerFromNpadIdType(aruid, npad_id); | ||
| 166 | if (!npad_resource.IsControllerSupported(aruid, controller.device->GetNpadStyleIndex())) { | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); | ||
| 170 | const auto controller_type = controller.device->GetNpadStyleIndex(); | ||
| 171 | const auto& body_colors = controller.device->GetColors(); | ||
| 172 | const auto& battery_level = controller.device->GetBattery(); | ||
| 173 | auto* shared_memory = controller.shared_memory; | ||
| 174 | if (controller_type == Core::HID::NpadStyleIndex::None) { | ||
| 175 | npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id); | ||
| 176 | return; | ||
| 177 | } | ||
| 178 | |||
| 179 | // Reset memory values | ||
| 180 | shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; | ||
| 181 | shared_memory->device_type.raw = 0; | ||
| 182 | shared_memory->system_properties.raw = 0; | ||
| 183 | shared_memory->joycon_color.attribute = ColorAttribute::NoController; | ||
| 184 | shared_memory->joycon_color.attribute = ColorAttribute::NoController; | ||
| 185 | shared_memory->fullkey_color = {}; | ||
| 186 | shared_memory->joycon_color.left = {}; | ||
| 187 | shared_memory->joycon_color.right = {}; | ||
| 188 | shared_memory->battery_level_dual = {}; | ||
| 189 | shared_memory->battery_level_left = {}; | ||
| 190 | shared_memory->battery_level_right = {}; | ||
| 191 | |||
| 192 | switch (controller_type) { | ||
| 193 | case Core::HID::NpadStyleIndex::None: | ||
| 194 | ASSERT(false); | ||
| 195 | break; | ||
| 196 | case Core::HID::NpadStyleIndex::ProController: | ||
| 197 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 198 | shared_memory->fullkey_color.fullkey = body_colors.fullkey; | ||
| 199 | shared_memory->battery_level_dual = battery_level.dual.battery_level; | ||
| 200 | shared_memory->style_tag.fullkey.Assign(1); | ||
| 201 | shared_memory->device_type.fullkey.Assign(1); | ||
| 202 | shared_memory->system_properties.is_vertical.Assign(1); | ||
| 203 | shared_memory->system_properties.use_plus.Assign(1); | ||
| 204 | shared_memory->system_properties.use_minus.Assign(1); | ||
| 205 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 206 | battery_level.dual.is_charging); | ||
| 207 | shared_memory->applet_footer_type = AppletFooterUiType::SwitchProController; | ||
| 208 | shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); | ||
| 209 | break; | ||
| 210 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 211 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 212 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 213 | shared_memory->fullkey_color.fullkey = body_colors.fullkey; | ||
| 214 | shared_memory->joycon_color.left = body_colors.left; | ||
| 215 | shared_memory->joycon_color.right = body_colors.right; | ||
| 216 | shared_memory->style_tag.handheld.Assign(1); | ||
| 217 | shared_memory->device_type.handheld_left.Assign(1); | ||
| 218 | shared_memory->device_type.handheld_right.Assign(1); | ||
| 219 | shared_memory->system_properties.is_vertical.Assign(1); | ||
| 220 | shared_memory->system_properties.use_plus.Assign(1); | ||
| 221 | shared_memory->system_properties.use_minus.Assign(1); | ||
| 222 | shared_memory->system_properties.use_directional_buttons.Assign(1); | ||
| 223 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 224 | battery_level.left.is_charging); | ||
| 225 | shared_memory->system_properties.is_charging_joy_left.Assign( | ||
| 226 | battery_level.left.is_charging); | ||
| 227 | shared_memory->system_properties.is_charging_joy_right.Assign( | ||
| 228 | battery_level.right.is_charging); | ||
| 229 | shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; | ||
| 230 | shared_memory->applet_footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; | ||
| 231 | shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); | ||
| 232 | break; | ||
| 233 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 234 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 235 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 236 | shared_memory->style_tag.joycon_dual.Assign(1); | ||
| 237 | if (controller.is_dual_left_connected) { | ||
| 238 | shared_memory->joycon_color.left = body_colors.left; | ||
| 239 | shared_memory->battery_level_left = battery_level.left.battery_level; | ||
| 240 | shared_memory->device_type.joycon_left.Assign(1); | ||
| 241 | shared_memory->system_properties.use_minus.Assign(1); | ||
| 242 | shared_memory->system_properties.is_charging_joy_left.Assign( | ||
| 243 | battery_level.left.is_charging); | ||
| 244 | shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1); | ||
| 245 | } | ||
| 246 | if (controller.is_dual_right_connected) { | ||
| 247 | shared_memory->joycon_color.right = body_colors.right; | ||
| 248 | shared_memory->battery_level_right = battery_level.right.battery_level; | ||
| 249 | shared_memory->device_type.joycon_right.Assign(1); | ||
| 250 | shared_memory->system_properties.use_plus.Assign(1); | ||
| 251 | shared_memory->system_properties.is_charging_joy_right.Assign( | ||
| 252 | battery_level.right.is_charging); | ||
| 253 | shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1); | ||
| 254 | } | ||
| 255 | shared_memory->system_properties.use_directional_buttons.Assign(1); | ||
| 256 | shared_memory->system_properties.is_vertical.Assign(1); | ||
| 257 | shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; | ||
| 258 | |||
| 259 | if (controller.is_dual_left_connected && controller.is_dual_right_connected) { | ||
| 260 | shared_memory->applet_footer_type = AppletFooterUiType::JoyDual; | ||
| 261 | shared_memory->fullkey_color.fullkey = body_colors.left; | ||
| 262 | shared_memory->battery_level_dual = battery_level.left.battery_level; | ||
| 263 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 264 | battery_level.left.is_charging); | ||
| 265 | } else if (controller.is_dual_left_connected) { | ||
| 266 | shared_memory->applet_footer_type = AppletFooterUiType::JoyDualLeftOnly; | ||
| 267 | shared_memory->fullkey_color.fullkey = body_colors.left; | ||
| 268 | shared_memory->battery_level_dual = battery_level.left.battery_level; | ||
| 269 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 270 | battery_level.left.is_charging); | ||
| 271 | } else { | ||
| 272 | shared_memory->applet_footer_type = AppletFooterUiType::JoyDualRightOnly; | ||
| 273 | shared_memory->fullkey_color.fullkey = body_colors.right; | ||
| 274 | shared_memory->battery_level_dual = battery_level.right.battery_level; | ||
| 275 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 276 | battery_level.right.is_charging); | ||
| 277 | } | ||
| 278 | break; | ||
| 279 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 280 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 281 | shared_memory->fullkey_color.fullkey = body_colors.left; | ||
| 282 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 283 | shared_memory->joycon_color.left = body_colors.left; | ||
| 284 | shared_memory->battery_level_dual = battery_level.left.battery_level; | ||
| 285 | shared_memory->style_tag.joycon_left.Assign(1); | ||
| 286 | shared_memory->device_type.joycon_left.Assign(1); | ||
| 287 | shared_memory->system_properties.is_horizontal.Assign(1); | ||
| 288 | shared_memory->system_properties.use_minus.Assign(1); | ||
| 289 | shared_memory->system_properties.is_charging_joy_left.Assign( | ||
| 290 | battery_level.left.is_charging); | ||
| 291 | shared_memory->applet_footer_type = AppletFooterUiType::JoyLeftHorizontal; | ||
| 292 | shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); | ||
| 293 | break; | ||
| 294 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 295 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 296 | shared_memory->fullkey_color.fullkey = body_colors.right; | ||
| 297 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 298 | shared_memory->joycon_color.right = body_colors.right; | ||
| 299 | shared_memory->battery_level_right = battery_level.right.battery_level; | ||
| 300 | shared_memory->style_tag.joycon_right.Assign(1); | ||
| 301 | shared_memory->device_type.joycon_right.Assign(1); | ||
| 302 | shared_memory->system_properties.is_horizontal.Assign(1); | ||
| 303 | shared_memory->system_properties.use_plus.Assign(1); | ||
| 304 | shared_memory->system_properties.is_charging_joy_right.Assign( | ||
| 305 | battery_level.right.is_charging); | ||
| 306 | shared_memory->applet_footer_type = AppletFooterUiType::JoyRightHorizontal; | ||
| 307 | shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); | ||
| 308 | break; | ||
| 309 | case Core::HID::NpadStyleIndex::GameCube: | ||
| 310 | shared_memory->style_tag.gamecube.Assign(1); | ||
| 311 | shared_memory->device_type.fullkey.Assign(1); | ||
| 312 | shared_memory->system_properties.is_vertical.Assign(1); | ||
| 313 | shared_memory->system_properties.use_plus.Assign(1); | ||
| 314 | break; | ||
| 315 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 316 | shared_memory->style_tag.palma.Assign(1); | ||
| 317 | shared_memory->device_type.palma.Assign(1); | ||
| 318 | shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); | ||
| 319 | break; | ||
| 320 | case Core::HID::NpadStyleIndex::NES: | ||
| 321 | shared_memory->style_tag.lark.Assign(1); | ||
| 322 | shared_memory->device_type.fullkey.Assign(1); | ||
| 323 | break; | ||
| 324 | case Core::HID::NpadStyleIndex::SNES: | ||
| 325 | shared_memory->style_tag.lucia.Assign(1); | ||
| 326 | shared_memory->device_type.fullkey.Assign(1); | ||
| 327 | shared_memory->applet_footer_type = AppletFooterUiType::Lucia; | ||
| 328 | break; | ||
| 329 | case Core::HID::NpadStyleIndex::N64: | ||
| 330 | shared_memory->style_tag.lagoon.Assign(1); | ||
| 331 | shared_memory->device_type.fullkey.Assign(1); | ||
| 332 | shared_memory->applet_footer_type = AppletFooterUiType::Lagon; | ||
| 333 | break; | ||
| 334 | case Core::HID::NpadStyleIndex::SegaGenesis: | ||
| 335 | shared_memory->style_tag.lager.Assign(1); | ||
| 336 | shared_memory->device_type.fullkey.Assign(1); | ||
| 337 | break; | ||
| 338 | default: | ||
| 339 | break; | ||
| 340 | } | ||
| 341 | |||
| 342 | controller.is_connected = true; | ||
| 343 | controller.device->Connect(); | ||
| 344 | controller.device->SetLedPattern(); | ||
| 345 | if (controller_type == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 346 | if (controller.is_dual_left_connected) { | ||
| 347 | controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::LeftIndex, | ||
| 348 | Common::Input::PollingMode::Active); | ||
| 349 | } | ||
| 350 | if (controller.is_dual_right_connected) { | ||
| 351 | controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 352 | Common::Input::PollingMode::Active); | ||
| 353 | } | ||
| 354 | } else { | ||
| 355 | controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, | ||
| 356 | Common::Input::PollingMode::Active); | ||
| 357 | } | ||
| 358 | |||
| 359 | npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id); | ||
| 360 | WriteEmptyEntry(controller.shared_memory); | ||
| 361 | hid_core.SetLastActiveController(npad_id); | ||
| 362 | } | ||
| 363 | |||
| 364 | void NPad::WriteEmptyEntry(NpadInternalState* npad) { | ||
| 365 | NPadGenericState dummy_pad_state{}; | ||
| 366 | NpadGcTriggerState dummy_gc_state{}; | ||
| 367 | dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 368 | npad->fullkey_lifo.WriteNextEntry(dummy_pad_state); | ||
| 369 | dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 370 | npad->handheld_lifo.WriteNextEntry(dummy_pad_state); | ||
| 371 | dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 372 | npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state); | ||
| 373 | dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 374 | npad->joy_left_lifo.WriteNextEntry(dummy_pad_state); | ||
| 375 | dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 376 | npad->joy_right_lifo.WriteNextEntry(dummy_pad_state); | ||
| 377 | dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 378 | npad->palma_lifo.WriteNextEntry(dummy_pad_state); | ||
| 379 | dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 380 | npad->system_ext_lifo.WriteNextEntry(dummy_pad_state); | ||
| 381 | dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; | ||
| 382 | npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); | ||
| 383 | } | ||
| 384 | |||
| 385 | void NPad::RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id) { | ||
| 386 | std::scoped_lock lock{*applet_resource_holder.shared_mutex}; | ||
| 387 | auto& controller = GetControllerFromNpadIdType(aruid, npad_id); | ||
| 388 | const auto controller_type = controller.device->GetNpadStyleIndex(); | ||
| 389 | |||
| 390 | if (!controller.device->IsConnected() && controller.is_connected) { | ||
| 391 | DisconnectNpad(aruid, npad_id); | ||
| 392 | return; | ||
| 393 | } | ||
| 394 | if (!controller.device->IsConnected()) { | ||
| 395 | return; | ||
| 396 | } | ||
| 397 | if (controller.device->IsConnected() && !controller.is_connected) { | ||
| 398 | InitNewlyAddedController(aruid, npad_id); | ||
| 399 | } | ||
| 400 | |||
| 401 | // This function is unique to yuzu for the turbo buttons and motion to work properly | ||
| 402 | controller.device->StatusUpdate(); | ||
| 403 | |||
| 404 | auto& pad_entry = controller.npad_pad_state; | ||
| 405 | auto& trigger_entry = controller.npad_trigger_state; | ||
| 406 | const auto button_state = controller.device->GetNpadButtons(); | ||
| 407 | const auto stick_state = controller.device->GetSticks(); | ||
| 408 | |||
| 409 | using btn = Core::HID::NpadButton; | ||
| 410 | pad_entry.npad_buttons.raw = btn::None; | ||
| 411 | if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) { | ||
| 412 | constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R | | ||
| 413 | btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp | | ||
| 414 | btn::StickRRight | btn::StickRDown; | ||
| 415 | pad_entry.npad_buttons.raw = button_state.raw & right_button_mask; | ||
| 416 | pad_entry.r_stick = stick_state.right; | ||
| 417 | } | ||
| 418 | |||
| 419 | if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) { | ||
| 420 | constexpr btn left_button_mask = | ||
| 421 | btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL | | ||
| 422 | btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown; | ||
| 423 | pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask; | ||
| 424 | pad_entry.l_stick = stick_state.left; | ||
| 425 | } | ||
| 426 | |||
| 427 | if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft || | ||
| 428 | controller_type == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 429 | pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); | ||
| 430 | pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); | ||
| 431 | } | ||
| 432 | |||
| 433 | if (controller_type == Core::HID::NpadStyleIndex::JoyconRight || | ||
| 434 | controller_type == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 435 | pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); | ||
| 436 | pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); | ||
| 437 | } | ||
| 438 | |||
| 439 | if (controller_type == Core::HID::NpadStyleIndex::GameCube) { | ||
| 440 | const auto& trigger_state = controller.device->GetTriggers(); | ||
| 441 | trigger_entry.l_analog = trigger_state.left; | ||
| 442 | trigger_entry.r_analog = trigger_state.right; | ||
| 443 | pad_entry.npad_buttons.zl.Assign(false); | ||
| 444 | pad_entry.npad_buttons.zr.Assign(button_state.r); | ||
| 445 | pad_entry.npad_buttons.l.Assign(button_state.zl); | ||
| 446 | pad_entry.npad_buttons.r.Assign(button_state.zr); | ||
| 447 | } | ||
| 448 | |||
| 449 | if (pad_entry.npad_buttons.raw != Core::HID::NpadButton::None) { | ||
| 450 | hid_core.SetLastActiveController(npad_id); | ||
| 451 | } | ||
| 452 | } | ||
| 453 | |||
| 454 | void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 455 | if (ref_counter == 0) { | ||
| 456 | return; | ||
| 457 | } | ||
| 458 | |||
| 459 | std::scoped_lock lock{*applet_resource_holder.shared_mutex}; | ||
| 460 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { | ||
| 461 | const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 462 | const auto aruid = data->aruid; | ||
| 463 | |||
| 464 | if (!data->flag.is_assigned) { | ||
| 465 | continue; | ||
| 466 | } | ||
| 467 | |||
| 468 | for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { | ||
| 469 | auto& controller = controller_data[aruid_index][i]; | ||
| 470 | controller.shared_memory = | ||
| 471 | &data->shared_memory_format->npad.npad_entry[i].internal_state; | ||
| 472 | auto* npad = controller.shared_memory; | ||
| 473 | |||
| 474 | const auto& controller_type = controller.device->GetNpadStyleIndex(); | ||
| 475 | |||
| 476 | if (controller_type == Core::HID::NpadStyleIndex::None || | ||
| 477 | !controller.device->IsConnected()) { | ||
| 478 | continue; | ||
| 479 | } | ||
| 480 | |||
| 481 | RequestPadStateUpdate(aruid, controller.device->GetNpadIdType()); | ||
| 482 | auto& pad_state = controller.npad_pad_state; | ||
| 483 | auto& libnx_state = controller.npad_libnx_state; | ||
| 484 | auto& trigger_state = controller.npad_trigger_state; | ||
| 485 | |||
| 486 | // LibNX exclusively uses this section, so we always update it since LibNX doesn't | ||
| 487 | // activate any controllers. | ||
| 488 | libnx_state.connection_status.raw = 0; | ||
| 489 | libnx_state.connection_status.is_connected.Assign(1); | ||
| 490 | switch (controller_type) { | ||
| 491 | case Core::HID::NpadStyleIndex::None: | ||
| 492 | ASSERT(false); | ||
| 493 | break; | ||
| 494 | case Core::HID::NpadStyleIndex::ProController: | ||
| 495 | case Core::HID::NpadStyleIndex::NES: | ||
| 496 | case Core::HID::NpadStyleIndex::SNES: | ||
| 497 | case Core::HID::NpadStyleIndex::N64: | ||
| 498 | case Core::HID::NpadStyleIndex::SegaGenesis: | ||
| 499 | pad_state.connection_status.raw = 0; | ||
| 500 | pad_state.connection_status.is_connected.Assign(1); | ||
| 501 | pad_state.connection_status.is_wired.Assign(1); | ||
| 502 | |||
| 503 | libnx_state.connection_status.is_wired.Assign(1); | ||
| 504 | pad_state.sampling_number = | ||
| 505 | npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 506 | npad->fullkey_lifo.WriteNextEntry(pad_state); | ||
| 507 | break; | ||
| 508 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 509 | pad_state.connection_status.raw = 0; | ||
| 510 | pad_state.connection_status.is_connected.Assign(1); | ||
| 511 | pad_state.connection_status.is_wired.Assign(1); | ||
| 512 | pad_state.connection_status.is_left_connected.Assign(1); | ||
| 513 | pad_state.connection_status.is_right_connected.Assign(1); | ||
| 514 | pad_state.connection_status.is_left_wired.Assign(1); | ||
| 515 | pad_state.connection_status.is_right_wired.Assign(1); | ||
| 516 | |||
| 517 | libnx_state.connection_status.is_wired.Assign(1); | ||
| 518 | libnx_state.connection_status.is_left_connected.Assign(1); | ||
| 519 | libnx_state.connection_status.is_right_connected.Assign(1); | ||
| 520 | libnx_state.connection_status.is_left_wired.Assign(1); | ||
| 521 | libnx_state.connection_status.is_right_wired.Assign(1); | ||
| 522 | pad_state.sampling_number = | ||
| 523 | npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 524 | npad->handheld_lifo.WriteNextEntry(pad_state); | ||
| 525 | break; | ||
| 526 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 527 | pad_state.connection_status.raw = 0; | ||
| 528 | pad_state.connection_status.is_connected.Assign(1); | ||
| 529 | if (controller.is_dual_left_connected) { | ||
| 530 | pad_state.connection_status.is_left_connected.Assign(1); | ||
| 531 | libnx_state.connection_status.is_left_connected.Assign(1); | ||
| 532 | } | ||
| 533 | if (controller.is_dual_right_connected) { | ||
| 534 | pad_state.connection_status.is_right_connected.Assign(1); | ||
| 535 | libnx_state.connection_status.is_right_connected.Assign(1); | ||
| 536 | } | ||
| 537 | |||
| 538 | pad_state.sampling_number = | ||
| 539 | npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 540 | npad->joy_dual_lifo.WriteNextEntry(pad_state); | ||
| 541 | break; | ||
| 542 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 543 | pad_state.connection_status.raw = 0; | ||
| 544 | pad_state.connection_status.is_connected.Assign(1); | ||
| 545 | pad_state.connection_status.is_left_connected.Assign(1); | ||
| 546 | |||
| 547 | libnx_state.connection_status.is_left_connected.Assign(1); | ||
| 548 | pad_state.sampling_number = | ||
| 549 | npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 550 | npad->joy_left_lifo.WriteNextEntry(pad_state); | ||
| 551 | break; | ||
| 552 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 553 | pad_state.connection_status.raw = 0; | ||
| 554 | pad_state.connection_status.is_connected.Assign(1); | ||
| 555 | pad_state.connection_status.is_right_connected.Assign(1); | ||
| 556 | |||
| 557 | libnx_state.connection_status.is_right_connected.Assign(1); | ||
| 558 | pad_state.sampling_number = | ||
| 559 | npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 560 | npad->joy_right_lifo.WriteNextEntry(pad_state); | ||
| 561 | break; | ||
| 562 | case Core::HID::NpadStyleIndex::GameCube: | ||
| 563 | pad_state.connection_status.raw = 0; | ||
| 564 | pad_state.connection_status.is_connected.Assign(1); | ||
| 565 | pad_state.connection_status.is_wired.Assign(1); | ||
| 566 | |||
| 567 | libnx_state.connection_status.is_wired.Assign(1); | ||
| 568 | pad_state.sampling_number = | ||
| 569 | npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 570 | trigger_state.sampling_number = | ||
| 571 | npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 572 | npad->fullkey_lifo.WriteNextEntry(pad_state); | ||
| 573 | npad->gc_trigger_lifo.WriteNextEntry(trigger_state); | ||
| 574 | break; | ||
| 575 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 576 | pad_state.connection_status.raw = 0; | ||
| 577 | pad_state.connection_status.is_connected.Assign(1); | ||
| 578 | pad_state.sampling_number = | ||
| 579 | npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 580 | npad->palma_lifo.WriteNextEntry(pad_state); | ||
| 581 | break; | ||
| 582 | default: | ||
| 583 | break; | ||
| 584 | } | ||
| 585 | |||
| 586 | libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; | ||
| 587 | libnx_state.l_stick = pad_state.l_stick; | ||
| 588 | libnx_state.r_stick = pad_state.r_stick; | ||
| 589 | npad->system_ext_lifo.WriteNextEntry(pad_state); | ||
| 590 | |||
| 591 | press_state |= static_cast<u64>(pad_state.npad_buttons.raw); | ||
| 592 | } | ||
| 593 | } | ||
| 594 | } | ||
| 595 | |||
| 596 | Result NPad::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set) { | ||
| 597 | std::scoped_lock lock{mutex}; | ||
| 598 | hid_core.SetSupportedStyleTag({supported_style_set}); | ||
| 599 | const Result result = npad_resource.SetSupportedNpadStyleSet(aruid, supported_style_set); | ||
| 600 | if (result.IsSuccess()) { | ||
| 601 | OnUpdate({}); | ||
| 602 | } | ||
| 603 | return result; | ||
| 604 | } | ||
| 605 | |||
| 606 | Result NPad::GetSupportedNpadStyleSet(u64 aruid, | ||
| 607 | Core::HID::NpadStyleSet& out_supported_style_set) const { | ||
| 608 | std::scoped_lock lock{mutex}; | ||
| 609 | const Result result = npad_resource.GetSupportedNpadStyleSet(out_supported_style_set, aruid); | ||
| 610 | |||
| 611 | if (result == ResultUndefinedStyleset) { | ||
| 612 | out_supported_style_set = Core::HID::NpadStyleSet::None; | ||
| 613 | return ResultSuccess; | ||
| 614 | } | ||
| 615 | |||
| 616 | return result; | ||
| 617 | } | ||
| 618 | |||
| 619 | Result NPad::GetMaskedSupportedNpadStyleSet( | ||
| 620 | u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const { | ||
| 621 | std::scoped_lock lock{mutex}; | ||
| 622 | const Result result = | ||
| 623 | npad_resource.GetMaskedSupportedNpadStyleSet(out_supported_style_set, aruid); | ||
| 624 | |||
| 625 | if (result == ResultUndefinedStyleset) { | ||
| 626 | out_supported_style_set = Core::HID::NpadStyleSet::None; | ||
| 627 | return ResultSuccess; | ||
| 628 | } | ||
| 629 | |||
| 630 | return result; | ||
| 631 | } | ||
| 632 | |||
| 633 | Result NPad::SetSupportedNpadIdType(u64 aruid, | ||
| 634 | std::span<const Core::HID::NpadIdType> supported_npad_list) { | ||
| 635 | std::scoped_lock lock{mutex}; | ||
| 636 | if (supported_npad_list.size() > MaxSupportedNpadIdTypes) { | ||
| 637 | return ResultInvalidArraySize; | ||
| 638 | } | ||
| 639 | |||
| 640 | Result result = npad_resource.SetSupportedNpadIdType(aruid, supported_npad_list); | ||
| 641 | |||
| 642 | if (result.IsSuccess()) { | ||
| 643 | OnUpdate({}); | ||
| 644 | } | ||
| 645 | |||
| 646 | return result; | ||
| 647 | } | ||
| 648 | |||
| 649 | Result NPad::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) { | ||
| 650 | std::scoped_lock lock{mutex}; | ||
| 651 | return npad_resource.SetNpadJoyHoldType(aruid, hold_type); | ||
| 652 | } | ||
| 653 | |||
| 654 | Result NPad::GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const { | ||
| 655 | std::scoped_lock lock{mutex}; | ||
| 656 | return npad_resource.GetNpadJoyHoldType(out_hold_type, aruid); | ||
| 657 | } | ||
| 658 | |||
| 659 | Result NPad::SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode) { | ||
| 660 | std::scoped_lock lock{mutex}; | ||
| 661 | Result result = npad_resource.SetNpadHandheldActivationMode(aruid, mode); | ||
| 662 | if (result.IsSuccess()) { | ||
| 663 | OnUpdate({}); | ||
| 664 | } | ||
| 665 | return result; | ||
| 666 | } | ||
| 667 | |||
| 668 | Result NPad::GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const { | ||
| 669 | std::scoped_lock lock{mutex}; | ||
| 670 | return npad_resource.GetNpadHandheldActivationMode(out_mode, aruid); | ||
| 671 | } | ||
| 672 | |||
| 673 | bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, | ||
| 674 | NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { | ||
| 675 | if (!IsNpadIdValid(npad_id)) { | ||
| 676 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 677 | return false; | ||
| 678 | } | ||
| 679 | |||
| 680 | auto& controller = GetControllerFromNpadIdType(aruid, npad_id); | ||
| 681 | if (controller.shared_memory->assignment_mode != assignment_mode) { | ||
| 682 | controller.shared_memory->assignment_mode = assignment_mode; | ||
| 683 | } | ||
| 684 | |||
| 685 | if (!controller.device->IsConnected()) { | ||
| 686 | return false; | ||
| 687 | } | ||
| 688 | |||
| 689 | if (assignment_mode == NpadJoyAssignmentMode::Dual) { | ||
| 690 | if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) { | ||
| 691 | DisconnectNpad(aruid, npad_id); | ||
| 692 | controller.is_dual_left_connected = true; | ||
| 693 | controller.is_dual_right_connected = false; | ||
| 694 | UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); | ||
| 695 | return false; | ||
| 696 | } | ||
| 697 | if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { | ||
| 698 | DisconnectNpad(aruid, npad_id); | ||
| 699 | controller.is_dual_left_connected = false; | ||
| 700 | controller.is_dual_right_connected = true; | ||
| 701 | UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); | ||
| 702 | return false; | ||
| 703 | } | ||
| 704 | return false; | ||
| 705 | } | ||
| 706 | |||
| 707 | // This is for NpadJoyAssignmentMode::Single | ||
| 708 | |||
| 709 | // Only JoyconDual get affected by this function | ||
| 710 | if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 711 | return false; | ||
| 712 | } | ||
| 713 | |||
| 714 | if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { | ||
| 715 | DisconnectNpad(aruid, npad_id); | ||
| 716 | UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); | ||
| 717 | return false; | ||
| 718 | } | ||
| 719 | if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { | ||
| 720 | DisconnectNpad(aruid, npad_id); | ||
| 721 | UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); | ||
| 722 | return false; | ||
| 723 | } | ||
| 724 | |||
| 725 | // We have two controllers connected to the same npad_id we need to split them | ||
| 726 | new_npad_id = hid_core.GetFirstDisconnectedNpadId(); | ||
| 727 | auto& controller_2 = GetControllerFromNpadIdType(aruid, new_npad_id); | ||
| 728 | DisconnectNpad(aruid, npad_id); | ||
| 729 | if (npad_device_type == NpadJoyDeviceType::Left) { | ||
| 730 | UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); | ||
| 731 | controller_2.is_dual_left_connected = false; | ||
| 732 | controller_2.is_dual_right_connected = true; | ||
| 733 | UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); | ||
| 734 | } else { | ||
| 735 | UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); | ||
| 736 | controller_2.is_dual_left_connected = true; | ||
| 737 | controller_2.is_dual_right_connected = false; | ||
| 738 | UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); | ||
| 739 | } | ||
| 740 | return true; | ||
| 741 | } | ||
| 742 | |||
| 743 | bool NPad::VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 744 | std::size_t device_index, | ||
| 745 | const Core::HID::VibrationValue& vibration_value) { | ||
| 746 | auto& controller = GetControllerFromNpadIdType(aruid, npad_id); | ||
| 747 | if (!controller.device->IsConnected()) { | ||
| 748 | return false; | ||
| 749 | } | ||
| 750 | |||
| 751 | if (!controller.device->IsVibrationEnabled(device_index)) { | ||
| 752 | if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || | ||
| 753 | controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { | ||
| 754 | // Send an empty vibration to stop any vibrations. | ||
| 755 | Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f}; | ||
| 756 | controller.device->SetVibration(device_index, vibration); | ||
| 757 | // Then reset the vibration value to its default value. | ||
| 758 | controller.vibration[device_index].latest_vibration_value = | ||
| 759 | Core::HID::DEFAULT_VIBRATION_VALUE; | ||
| 760 | } | ||
| 761 | |||
| 762 | return false; | ||
| 763 | } | ||
| 764 | |||
| 765 | if (!Settings::values.enable_accurate_vibrations.GetValue()) { | ||
| 766 | using std::chrono::duration_cast; | ||
| 767 | using std::chrono::milliseconds; | ||
| 768 | using std::chrono::steady_clock; | ||
| 769 | |||
| 770 | const auto now = steady_clock::now(); | ||
| 771 | |||
| 772 | // Filter out non-zero vibrations that are within 15ms of each other. | ||
| 773 | if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) && | ||
| 774 | duration_cast<milliseconds>( | ||
| 775 | now - controller.vibration[device_index].last_vibration_timepoint) < | ||
| 776 | milliseconds(15)) { | ||
| 777 | return false; | ||
| 778 | } | ||
| 779 | |||
| 780 | controller.vibration[device_index].last_vibration_timepoint = now; | ||
| 781 | } | ||
| 782 | |||
| 783 | Core::HID::VibrationValue vibration{ | ||
| 784 | vibration_value.low_amplitude, vibration_value.low_frequency, | ||
| 785 | vibration_value.high_amplitude, vibration_value.high_frequency}; | ||
| 786 | return controller.device->SetVibration(device_index, vibration); | ||
| 787 | } | ||
| 788 | |||
| 789 | void NPad::VibrateController(u64 aruid, | ||
| 790 | const Core::HID::VibrationDeviceHandle& vibration_device_handle, | ||
| 791 | const Core::HID::VibrationValue& vibration_value) { | ||
| 792 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { | ||
| 793 | return; | ||
| 794 | } | ||
| 795 | |||
| 796 | if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { | ||
| 797 | return; | ||
| 798 | } | ||
| 799 | |||
| 800 | auto& controller = GetControllerFromHandle(aruid, vibration_device_handle); | ||
| 801 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); | ||
| 802 | |||
| 803 | if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) { | ||
| 804 | return; | ||
| 805 | } | ||
| 806 | |||
| 807 | if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) { | ||
| 808 | ASSERT_MSG(false, "DeviceIndex should never be None!"); | ||
| 809 | return; | ||
| 810 | } | ||
| 811 | |||
| 812 | // Some games try to send mismatched parameters in the device handle, block these. | ||
| 813 | if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft && | ||
| 814 | (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight || | ||
| 815 | vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) || | ||
| 816 | (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight && | ||
| 817 | (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft || | ||
| 818 | vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) { | ||
| 819 | return; | ||
| 820 | } | ||
| 821 | |||
| 822 | // Filter out vibrations with equivalent values to reduce unnecessary state changes. | ||
| 823 | if (vibration_value.low_amplitude == | ||
| 824 | controller.vibration[device_index].latest_vibration_value.low_amplitude && | ||
| 825 | vibration_value.high_amplitude == | ||
| 826 | controller.vibration[device_index].latest_vibration_value.high_amplitude) { | ||
| 827 | return; | ||
| 828 | } | ||
| 829 | |||
| 830 | if (VibrateControllerAtIndex(aruid, controller.device->GetNpadIdType(), device_index, | ||
| 831 | vibration_value)) { | ||
| 832 | controller.vibration[device_index].latest_vibration_value = vibration_value; | ||
| 833 | } | ||
| 834 | } | ||
| 835 | |||
| 836 | void NPad::VibrateControllers( | ||
| 837 | u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, | ||
| 838 | std::span<const Core::HID::VibrationValue> vibration_values) { | ||
| 839 | if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { | ||
| 840 | return; | ||
| 841 | } | ||
| 842 | |||
| 843 | ASSERT_OR_EXECUTE_MSG( | ||
| 844 | vibration_device_handles.size() == vibration_values.size(), { return; }, | ||
| 845 | "The amount of device handles does not match with the amount of vibration values," | ||
| 846 | "this is undefined behavior!"); | ||
| 847 | |||
| 848 | for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) { | ||
| 849 | VibrateController(aruid, vibration_device_handles[i], vibration_values[i]); | ||
| 850 | } | ||
| 851 | } | ||
| 852 | |||
| 853 | Core::HID::VibrationValue NPad::GetLastVibration( | ||
| 854 | u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | ||
| 855 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { | ||
| 856 | return {}; | ||
| 857 | } | ||
| 858 | |||
| 859 | const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle); | ||
| 860 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); | ||
| 861 | return controller.vibration[device_index].latest_vibration_value; | ||
| 862 | } | ||
| 863 | |||
| 864 | void NPad::InitializeVibrationDevice( | ||
| 865 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) { | ||
| 866 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { | ||
| 867 | return; | ||
| 868 | } | ||
| 869 | |||
| 870 | const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid(); | ||
| 871 | const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id); | ||
| 872 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); | ||
| 873 | InitializeVibrationDeviceAtIndex(aruid, npad_index, device_index); | ||
| 874 | } | ||
| 875 | |||
| 876 | void NPad::InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 877 | std::size_t device_index) { | ||
| 878 | auto& controller = GetControllerFromNpadIdType(aruid, npad_id); | ||
| 879 | if (!Settings::values.vibration_enabled.GetValue()) { | ||
| 880 | controller.vibration[device_index].device_mounted = false; | ||
| 881 | return; | ||
| 882 | } | ||
| 883 | |||
| 884 | controller.vibration[device_index].device_mounted = | ||
| 885 | controller.device->IsVibrationEnabled(device_index); | ||
| 886 | } | ||
| 887 | |||
| 888 | void NPad::SetPermitVibrationSession(bool permit_vibration_session) { | ||
| 889 | permit_vibration_session_enabled = permit_vibration_session; | ||
| 890 | } | ||
| 891 | |||
| 892 | bool NPad::IsVibrationDeviceMounted( | ||
| 893 | u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | ||
| 894 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { | ||
| 895 | return false; | ||
| 896 | } | ||
| 897 | |||
| 898 | const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle); | ||
| 899 | const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); | ||
| 900 | return controller.vibration[device_index].device_mounted; | ||
| 901 | } | ||
| 902 | |||
| 903 | Result NPad::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event, | ||
| 904 | Core::HID::NpadIdType npad_id) { | ||
| 905 | std::scoped_lock lock{mutex}; | ||
| 906 | return npad_resource.AcquireNpadStyleSetUpdateEventHandle(aruid, out_event, npad_id); | ||
| 907 | } | ||
| 908 | |||
| 909 | void NPad::AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller, | ||
| 910 | Core::HID::NpadIdType npad_id) { | ||
| 911 | UpdateControllerAt(aruid, controller, npad_id, true); | ||
| 912 | } | ||
| 913 | |||
| 914 | void NPad::UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex type, | ||
| 915 | Core::HID::NpadIdType npad_id, bool connected) { | ||
| 916 | auto& controller = GetControllerFromNpadIdType(aruid, npad_id); | ||
| 917 | if (!connected) { | ||
| 918 | DisconnectNpad(aruid, npad_id); | ||
| 919 | return; | ||
| 920 | } | ||
| 921 | |||
| 922 | controller.device->SetNpadStyleIndex(type); | ||
| 923 | InitNewlyAddedController(aruid, npad_id); | ||
| 924 | } | ||
| 925 | |||
| 926 | Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) { | ||
| 927 | if (!IsNpadIdValid(npad_id)) { | ||
| 928 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 929 | return ResultInvalidNpadId; | ||
| 930 | } | ||
| 931 | |||
| 932 | LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); | ||
| 933 | auto& controller = GetControllerFromNpadIdType(aruid, npad_id); | ||
| 934 | for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) { | ||
| 935 | // Send an empty vibration to stop any vibrations. | ||
| 936 | VibrateControllerAtIndex(aruid, npad_id, device_idx, {}); | ||
| 937 | controller.vibration[device_idx].device_mounted = false; | ||
| 938 | } | ||
| 939 | |||
| 940 | auto* shared_memory = controller.shared_memory; | ||
| 941 | // Don't reset shared_memory->assignment_mode this value is persistent | ||
| 942 | shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out | ||
| 943 | shared_memory->device_type.raw = 0; | ||
| 944 | shared_memory->system_properties.raw = 0; | ||
| 945 | shared_memory->button_properties.raw = 0; | ||
| 946 | shared_memory->sixaxis_fullkey_properties.raw = 0; | ||
| 947 | shared_memory->sixaxis_handheld_properties.raw = 0; | ||
| 948 | shared_memory->sixaxis_dual_left_properties.raw = 0; | ||
| 949 | shared_memory->sixaxis_dual_right_properties.raw = 0; | ||
| 950 | shared_memory->sixaxis_left_properties.raw = 0; | ||
| 951 | shared_memory->sixaxis_right_properties.raw = 0; | ||
| 952 | shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty; | ||
| 953 | shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty; | ||
| 954 | shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty; | ||
| 955 | shared_memory->fullkey_color = { | ||
| 956 | .attribute = ColorAttribute::NoController, | ||
| 957 | .fullkey = {}, | ||
| 958 | }; | ||
| 959 | shared_memory->joycon_color = { | ||
| 960 | .attribute = ColorAttribute::NoController, | ||
| 961 | .left = {}, | ||
| 962 | .right = {}, | ||
| 963 | }; | ||
| 964 | shared_memory->applet_footer_type = AppletFooterUiType::None; | ||
| 965 | |||
| 966 | controller.is_dual_left_connected = true; | ||
| 967 | controller.is_dual_right_connected = true; | ||
| 968 | controller.is_connected = false; | ||
| 969 | controller.device->Disconnect(); | ||
| 970 | npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id); | ||
| 971 | WriteEmptyEntry(shared_memory); | ||
| 972 | return ResultSuccess; | ||
| 973 | } | ||
| 974 | |||
| 975 | Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor( | ||
| 976 | u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 977 | bool& is_firmware_available) const { | ||
| 978 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 979 | if (is_valid.IsError()) { | ||
| 980 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 981 | return is_valid; | ||
| 982 | } | ||
| 983 | |||
| 984 | const auto& sixaxis_properties = GetSixaxisProperties(aruid, sixaxis_handle); | ||
| 985 | is_firmware_available = sixaxis_properties.is_firmware_update_available != 0; | ||
| 986 | return ResultSuccess; | ||
| 987 | } | ||
| 988 | |||
| 989 | Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( | ||
| 990 | u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | ||
| 991 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 992 | if (is_valid.IsError()) { | ||
| 993 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 994 | return is_valid; | ||
| 995 | } | ||
| 996 | |||
| 997 | auto& sixaxis_properties = GetSixaxisProperties(aruid, sixaxis_handle); | ||
| 998 | sixaxis_properties.is_newly_assigned.Assign(0); | ||
| 999 | |||
| 1000 | return ResultSuccess; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | Result NPad::MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1, | ||
| 1004 | Core::HID::NpadIdType npad_id_2) { | ||
| 1005 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { | ||
| 1006 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, | ||
| 1007 | npad_id_2); | ||
| 1008 | return ResultInvalidNpadId; | ||
| 1009 | } | ||
| 1010 | auto& controller_1 = GetControllerFromNpadIdType(aruid, npad_id_1); | ||
| 1011 | auto& controller_2 = GetControllerFromNpadIdType(aruid, npad_id_2); | ||
| 1012 | auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); | ||
| 1013 | auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); | ||
| 1014 | |||
| 1015 | // Simplify this code by converting dualjoycon with only a side connected to single joycons | ||
| 1016 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 1017 | if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) { | ||
| 1018 | controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft; | ||
| 1019 | } | ||
| 1020 | if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) { | ||
| 1021 | controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight; | ||
| 1022 | } | ||
| 1023 | } | ||
| 1024 | if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 1025 | if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) { | ||
| 1026 | controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft; | ||
| 1027 | } | ||
| 1028 | if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) { | ||
| 1029 | controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight; | ||
| 1030 | } | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | // Invalid merge errors | ||
| 1034 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual || | ||
| 1035 | controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 1036 | return NpadIsDualJoycon; | ||
| 1037 | } | ||
| 1038 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && | ||
| 1039 | controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) { | ||
| 1040 | return NpadIsSameType; | ||
| 1041 | } | ||
| 1042 | if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight && | ||
| 1043 | controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) { | ||
| 1044 | return NpadIsSameType; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | // These exceptions are handled as if they where dual joycon | ||
| 1048 | if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft && | ||
| 1049 | controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) { | ||
| 1050 | return NpadIsDualJoycon; | ||
| 1051 | } | ||
| 1052 | if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft && | ||
| 1053 | controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) { | ||
| 1054 | return NpadIsDualJoycon; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | // Disconnect the joycons and connect them as dual joycon at the first index. | ||
| 1058 | DisconnectNpad(aruid, npad_id_1); | ||
| 1059 | DisconnectNpad(aruid, npad_id_2); | ||
| 1060 | controller_1.is_dual_left_connected = true; | ||
| 1061 | controller_1.is_dual_right_connected = true; | ||
| 1062 | AddNewControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); | ||
| 1063 | return ResultSuccess; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | Result NPad::StartLrAssignmentMode(u64 aruid) { | ||
| 1067 | std::scoped_lock lock{mutex}; | ||
| 1068 | bool is_enabled{}; | ||
| 1069 | Result result = npad_resource.GetLrAssignmentMode(is_enabled, aruid); | ||
| 1070 | if (result.IsSuccess() && is_enabled == false) { | ||
| 1071 | result = npad_resource.SetLrAssignmentMode(aruid, true); | ||
| 1072 | } | ||
| 1073 | return result; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | Result NPad::StopLrAssignmentMode(u64 aruid) { | ||
| 1077 | std::scoped_lock lock{mutex}; | ||
| 1078 | bool is_enabled{}; | ||
| 1079 | Result result = npad_resource.GetLrAssignmentMode(is_enabled, aruid); | ||
| 1080 | if (result.IsSuccess() && is_enabled == true) { | ||
| 1081 | result = npad_resource.SetLrAssignmentMode(aruid, false); | ||
| 1082 | } | ||
| 1083 | return result; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | Result NPad::SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1, | ||
| 1087 | Core::HID::NpadIdType npad_id_2) { | ||
| 1088 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { | ||
| 1089 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, | ||
| 1090 | npad_id_2); | ||
| 1091 | return ResultInvalidNpadId; | ||
| 1092 | } | ||
| 1093 | if (npad_id_1 == Core::HID::NpadIdType::Handheld || | ||
| 1094 | npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other || | ||
| 1095 | npad_id_2 == Core::HID::NpadIdType::Other) { | ||
| 1096 | return ResultSuccess; | ||
| 1097 | } | ||
| 1098 | const auto& controller_1 = GetControllerFromNpadIdType(aruid, npad_id_1).device; | ||
| 1099 | const auto& controller_2 = GetControllerFromNpadIdType(aruid, npad_id_2).device; | ||
| 1100 | const auto type_index_1 = controller_1->GetNpadStyleIndex(); | ||
| 1101 | const auto type_index_2 = controller_2->GetNpadStyleIndex(); | ||
| 1102 | const auto is_connected_1 = controller_1->IsConnected(); | ||
| 1103 | const auto is_connected_2 = controller_2->IsConnected(); | ||
| 1104 | |||
| 1105 | if (!npad_resource.IsControllerSupported(aruid, type_index_1) && is_connected_1) { | ||
| 1106 | return ResultNpadNotConnected; | ||
| 1107 | } | ||
| 1108 | if (!npad_resource.IsControllerSupported(aruid, type_index_2) && is_connected_2) { | ||
| 1109 | return ResultNpadNotConnected; | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | UpdateControllerAt(aruid, type_index_2, npad_id_1, is_connected_2); | ||
| 1113 | UpdateControllerAt(aruid, type_index_1, npad_id_2, is_connected_1); | ||
| 1114 | |||
| 1115 | return ResultSuccess; | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const { | ||
| 1119 | if (!IsNpadIdValid(npad_id)) { | ||
| 1120 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 1121 | return ResultInvalidNpadId; | ||
| 1122 | } | ||
| 1123 | const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid(); | ||
| 1124 | const auto& controller = GetControllerFromNpadIdType(aruid, npad_id).device; | ||
| 1125 | pattern = controller->GetLedPattern(); | ||
| 1126 | return ResultSuccess; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid, | ||
| 1130 | Core::HID::NpadIdType npad_id) const { | ||
| 1131 | std::scoped_lock lock{mutex}; | ||
| 1132 | return npad_resource.GetHomeProtectionEnabled(out_is_enabled, aruid, npad_id); | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | Result NPad::EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 1136 | bool is_enabled) { | ||
| 1137 | std::scoped_lock lock{mutex}; | ||
| 1138 | return npad_resource.SetHomeProtectionEnabled(aruid, npad_id, is_enabled); | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | void NPad::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) { | ||
| 1142 | std::scoped_lock lock{mutex}; | ||
| 1143 | npad_resource.SetNpadAnalogStickUseCenterClamp(aruid, is_enabled); | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | void NPad::ClearAllConnectedControllers() { | ||
| 1147 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 1148 | for (auto& controller : controller_data[aruid_index]) { | ||
| 1149 | if (controller.device->IsConnected() && | ||
| 1150 | controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { | ||
| 1151 | controller.device->Disconnect(); | ||
| 1152 | controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); | ||
| 1153 | } | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | void NPad::DisconnectAllConnectedControllers() { | ||
| 1159 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 1160 | for (auto& controller : controller_data[aruid_index]) { | ||
| 1161 | controller.device->Disconnect(); | ||
| 1162 | } | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | void NPad::ConnectAllDisconnectedControllers() { | ||
| 1167 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 1168 | for (auto& controller : controller_data[aruid_index]) { | ||
| 1169 | if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && | ||
| 1170 | !controller.device->IsConnected()) { | ||
| 1171 | controller.device->Connect(); | ||
| 1172 | } | ||
| 1173 | } | ||
| 1174 | } | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | void NPad::ClearAllControllers() { | ||
| 1178 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 1179 | for (auto& controller : controller_data[aruid_index]) { | ||
| 1180 | controller.device->Disconnect(); | ||
| 1181 | controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); | ||
| 1182 | } | ||
| 1183 | } | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | Core::HID::NpadButton NPad::GetAndResetPressState() { | ||
| 1187 | return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | Result NPad::ApplyNpadSystemCommonPolicy(u64 aruid) { | ||
| 1191 | std::scoped_lock lock{mutex}; | ||
| 1192 | const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, false); | ||
| 1193 | if (result.IsSuccess()) { | ||
| 1194 | OnUpdate({}); | ||
| 1195 | } | ||
| 1196 | return result; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | Result NPad::ApplyNpadSystemCommonPolicyFull(u64 aruid) { | ||
| 1200 | std::scoped_lock lock{mutex}; | ||
| 1201 | const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, true); | ||
| 1202 | if (result.IsSuccess()) { | ||
| 1203 | OnUpdate({}); | ||
| 1204 | } | ||
| 1205 | return result; | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | Result NPad::ClearNpadSystemCommonPolicy(u64 aruid) { | ||
| 1209 | std::scoped_lock lock{mutex}; | ||
| 1210 | const Result result = npad_resource.ClearNpadSystemCommonPolicy(aruid); | ||
| 1211 | if (result.IsSuccess()) { | ||
| 1212 | OnUpdate({}); | ||
| 1213 | } | ||
| 1214 | return result; | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | void NPad::SetRevision(u64 aruid, NpadRevision revision) { | ||
| 1218 | npad_resource.SetNpadRevision(aruid, revision); | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | NpadRevision NPad::GetRevision(u64 aruid) { | ||
| 1222 | return npad_resource.GetNpadRevision(aruid); | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | Result NPad::RegisterAppletResourceUserId(u64 aruid) { | ||
| 1226 | return npad_resource.RegisterAppletResourceUserId(aruid); | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | void NPad::UnregisterAppletResourceUserId(u64 aruid) { | ||
| 1230 | npad_resource.UnregisterAppletResourceUserId(aruid); | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, | ||
| 1234 | std::recursive_mutex* shared_mutex) { | ||
| 1235 | applet_resource_holder.applet_resource = resource; | ||
| 1236 | applet_resource_holder.shared_mutex = shared_mutex; | ||
| 1237 | applet_resource_holder.shared_npad_resource = &npad_resource; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | NPad::NpadControllerData& NPad::GetControllerFromHandle( | ||
| 1241 | u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) { | ||
| 1242 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 1243 | return GetControllerFromNpadIdType(aruid, npad_id); | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | const NPad::NpadControllerData& NPad::GetControllerFromHandle( | ||
| 1247 | u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const { | ||
| 1248 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 1249 | return GetControllerFromNpadIdType(aruid, npad_id); | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | NPad::NpadControllerData& NPad::GetControllerFromHandle( | ||
| 1253 | u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) { | ||
| 1254 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 1255 | return GetControllerFromNpadIdType(aruid, npad_id); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | const NPad::NpadControllerData& NPad::GetControllerFromHandle( | ||
| 1259 | u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const { | ||
| 1260 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 1261 | return GetControllerFromNpadIdType(aruid, npad_id); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(u64 aruid, | ||
| 1265 | Core::HID::NpadIdType npad_id) { | ||
| 1266 | if (!IsNpadIdValid(npad_id)) { | ||
| 1267 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 1268 | npad_id = Core::HID::NpadIdType::Player1; | ||
| 1269 | } | ||
| 1270 | const auto npad_index = NpadIdTypeToIndex(npad_id); | ||
| 1271 | const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid); | ||
| 1272 | return controller_data[aruid_index][npad_index]; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType( | ||
| 1276 | u64 aruid, Core::HID::NpadIdType npad_id) const { | ||
| 1277 | if (!IsNpadIdValid(npad_id)) { | ||
| 1278 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 1279 | npad_id = Core::HID::NpadIdType::Player1; | ||
| 1280 | } | ||
| 1281 | const auto npad_index = NpadIdTypeToIndex(npad_id); | ||
| 1282 | const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid); | ||
| 1283 | return controller_data[aruid_index][npad_index]; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( | ||
| 1287 | u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | ||
| 1288 | auto& controller = GetControllerFromHandle(aruid, sixaxis_handle); | ||
| 1289 | switch (sixaxis_handle.npad_type) { | ||
| 1290 | case Core::HID::NpadStyleIndex::ProController: | ||
| 1291 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 1292 | return controller.shared_memory->sixaxis_fullkey_properties; | ||
| 1293 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 1294 | return controller.shared_memory->sixaxis_handheld_properties; | ||
| 1295 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1296 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 1297 | return controller.shared_memory->sixaxis_dual_left_properties; | ||
| 1298 | } | ||
| 1299 | return controller.shared_memory->sixaxis_dual_right_properties; | ||
| 1300 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 1301 | return controller.shared_memory->sixaxis_left_properties; | ||
| 1302 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 1303 | return controller.shared_memory->sixaxis_right_properties; | ||
| 1304 | default: | ||
| 1305 | return controller.shared_memory->sixaxis_fullkey_properties; | ||
| 1306 | } | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( | ||
| 1310 | u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { | ||
| 1311 | const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle); | ||
| 1312 | switch (sixaxis_handle.npad_type) { | ||
| 1313 | case Core::HID::NpadStyleIndex::ProController: | ||
| 1314 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 1315 | return controller.shared_memory->sixaxis_fullkey_properties; | ||
| 1316 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 1317 | return controller.shared_memory->sixaxis_handheld_properties; | ||
| 1318 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1319 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 1320 | return controller.shared_memory->sixaxis_dual_left_properties; | ||
| 1321 | } | ||
| 1322 | return controller.shared_memory->sixaxis_dual_right_properties; | ||
| 1323 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 1324 | return controller.shared_memory->sixaxis_left_properties; | ||
| 1325 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 1326 | return controller.shared_memory->sixaxis_right_properties; | ||
| 1327 | default: | ||
| 1328 | return controller.shared_memory->sixaxis_fullkey_properties; | ||
| 1329 | } | ||
| 1330 | } | ||
| 1331 | |||
| 1332 | AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { | ||
| 1333 | const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid(); | ||
| 1334 | const auto& shared_memory = GetControllerFromNpadIdType(aruid, npad_id).shared_memory; | ||
| 1335 | |||
| 1336 | return { | ||
| 1337 | .ui_variant = 0, | ||
| 1338 | .footer = shared_memory->applet_footer_type, | ||
| 1339 | }; | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h new file mode 100644 index 000000000..58f8c7acf --- /dev/null +++ b/src/hid_core/resources/npad/npad.h | |||
| @@ -0,0 +1,214 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <atomic> | ||
| 8 | #include <mutex> | ||
| 9 | #include <span> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "hid_core/hid_types.h" | ||
| 13 | #include "hid_core/resources/controller_base.h" | ||
| 14 | #include "hid_core/resources/npad/npad_resource.h" | ||
| 15 | #include "hid_core/resources/npad/npad_types.h" | ||
| 16 | |||
| 17 | namespace Core::HID { | ||
| 18 | class EmulatedController; | ||
| 19 | enum class ControllerTriggerType; | ||
| 20 | } // namespace Core::HID | ||
| 21 | |||
| 22 | namespace Kernel { | ||
| 23 | class KEvent; | ||
| 24 | class KReadableEvent; | ||
| 25 | } // namespace Kernel | ||
| 26 | |||
| 27 | namespace Service::KernelHelpers { | ||
| 28 | class ServiceContext; | ||
| 29 | } // namespace Service::KernelHelpers | ||
| 30 | |||
| 31 | union Result; | ||
| 32 | |||
| 33 | namespace Service::HID { | ||
| 34 | class AppletResource; | ||
| 35 | struct NpadInternalState; | ||
| 36 | struct NpadSixAxisSensorLifo; | ||
| 37 | struct NpadSharedMemoryFormat; | ||
| 38 | |||
| 39 | class NPad final { | ||
| 40 | public: | ||
| 41 | explicit NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_); | ||
| 42 | ~NPad(); | ||
| 43 | |||
| 44 | Result Activate(); | ||
| 45 | Result Activate(u64 aruid); | ||
| 46 | |||
| 47 | Result ActivateNpadResource(); | ||
| 48 | Result ActivateNpadResource(u64 aruid); | ||
| 49 | |||
| 50 | // When the controller is requesting an update for the shared memory | ||
| 51 | void OnUpdate(const Core::Timing::CoreTiming& core_timing); | ||
| 52 | |||
| 53 | Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set); | ||
| 54 | Result GetSupportedNpadStyleSet(u64 aruid, | ||
| 55 | Core::HID::NpadStyleSet& out_supported_style_set) const; | ||
| 56 | Result GetMaskedSupportedNpadStyleSet(u64 aruid, | ||
| 57 | Core::HID::NpadStyleSet& out_supported_style_set) const; | ||
| 58 | |||
| 59 | Result SetSupportedNpadIdType(u64 aruid, | ||
| 60 | std::span<const Core::HID::NpadIdType> supported_npad_list); | ||
| 61 | |||
| 62 | Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type); | ||
| 63 | Result GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const; | ||
| 64 | |||
| 65 | Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode); | ||
| 66 | Result GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const; | ||
| 67 | |||
| 68 | bool SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, | ||
| 69 | NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode); | ||
| 70 | |||
| 71 | bool VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 72 | std::size_t device_index, | ||
| 73 | const Core::HID::VibrationValue& vibration_value); | ||
| 74 | |||
| 75 | void VibrateController(u64 aruid, | ||
| 76 | const Core::HID::VibrationDeviceHandle& vibration_device_handle, | ||
| 77 | const Core::HID::VibrationValue& vibration_value); | ||
| 78 | |||
| 79 | void VibrateControllers( | ||
| 80 | u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, | ||
| 81 | std::span<const Core::HID::VibrationValue> vibration_values); | ||
| 82 | |||
| 83 | Core::HID::VibrationValue GetLastVibration( | ||
| 84 | u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; | ||
| 85 | |||
| 86 | void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle); | ||
| 87 | |||
| 88 | void InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 89 | std::size_t device_index); | ||
| 90 | |||
| 91 | void SetPermitVibrationSession(bool permit_vibration_session); | ||
| 92 | |||
| 93 | bool IsVibrationDeviceMounted( | ||
| 94 | u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; | ||
| 95 | |||
| 96 | Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event, | ||
| 97 | Core::HID::NpadIdType npad_id); | ||
| 98 | |||
| 99 | // Adds a new controller at an index. | ||
| 100 | void AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller, | ||
| 101 | Core::HID::NpadIdType npad_id); | ||
| 102 | // Adds a new controller at an index with connection status. | ||
| 103 | void UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller, | ||
| 104 | Core::HID::NpadIdType npad_id, bool connected); | ||
| 105 | |||
| 106 | Result DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id); | ||
| 107 | |||
| 108 | Result IsFirmwareUpdateAvailableForSixAxisSensor( | ||
| 109 | u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 110 | bool& is_firmware_available) const; | ||
| 111 | Result ResetIsSixAxisSensorDeviceNewlyAssigned( | ||
| 112 | u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle); | ||
| 113 | |||
| 114 | Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; | ||
| 115 | |||
| 116 | Result IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid, | ||
| 117 | Core::HID::NpadIdType npad_id) const; | ||
| 118 | Result EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 119 | bool is_enabled); | ||
| 120 | |||
| 121 | void SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled); | ||
| 122 | void ClearAllConnectedControllers(); | ||
| 123 | void DisconnectAllConnectedControllers(); | ||
| 124 | void ConnectAllDisconnectedControllers(); | ||
| 125 | void ClearAllControllers(); | ||
| 126 | |||
| 127 | Result MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1, | ||
| 128 | Core::HID::NpadIdType npad_id_2); | ||
| 129 | Result StartLrAssignmentMode(u64 aruid); | ||
| 130 | Result StopLrAssignmentMode(u64 aruid); | ||
| 131 | Result SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1, | ||
| 132 | Core::HID::NpadIdType npad_id_2); | ||
| 133 | |||
| 134 | // Logical OR for all buttons presses on all controllers | ||
| 135 | // Specifically for cheat engine and other features. | ||
| 136 | Core::HID::NpadButton GetAndResetPressState(); | ||
| 137 | |||
| 138 | Result ApplyNpadSystemCommonPolicy(u64 aruid); | ||
| 139 | Result ApplyNpadSystemCommonPolicyFull(u64 aruid); | ||
| 140 | Result ClearNpadSystemCommonPolicy(u64 aruid); | ||
| 141 | |||
| 142 | void SetRevision(u64 aruid, NpadRevision revision); | ||
| 143 | NpadRevision GetRevision(u64 aruid); | ||
| 144 | |||
| 145 | Result RegisterAppletResourceUserId(u64 aruid); | ||
| 146 | void UnregisterAppletResourceUserId(u64 aruid); | ||
| 147 | void SetNpadExternals(std::shared_ptr<AppletResource> resource, | ||
| 148 | std::recursive_mutex* shared_mutex); | ||
| 149 | |||
| 150 | AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); | ||
| 151 | |||
| 152 | private: | ||
| 153 | struct VibrationData { | ||
| 154 | bool device_mounted{}; | ||
| 155 | Core::HID::VibrationValue latest_vibration_value{}; | ||
| 156 | std::chrono::steady_clock::time_point last_vibration_timepoint{}; | ||
| 157 | }; | ||
| 158 | |||
| 159 | struct NpadControllerData { | ||
| 160 | NpadInternalState* shared_memory = nullptr; | ||
| 161 | Core::HID::EmulatedController* device = nullptr; | ||
| 162 | |||
| 163 | std::array<VibrationData, 2> vibration{}; | ||
| 164 | bool is_connected{}; | ||
| 165 | |||
| 166 | // Dual joycons can have only one side connected | ||
| 167 | bool is_dual_left_connected{true}; | ||
| 168 | bool is_dual_right_connected{true}; | ||
| 169 | |||
| 170 | // Current pad state | ||
| 171 | NPadGenericState npad_pad_state{}; | ||
| 172 | NPadGenericState npad_libnx_state{}; | ||
| 173 | NpadGcTriggerState npad_trigger_state{}; | ||
| 174 | int callback_key{}; | ||
| 175 | }; | ||
| 176 | |||
| 177 | void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx); | ||
| 178 | void InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id); | ||
| 179 | void RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id); | ||
| 180 | void WriteEmptyEntry(NpadInternalState* npad); | ||
| 181 | |||
| 182 | NpadControllerData& GetControllerFromHandle( | ||
| 183 | u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle); | ||
| 184 | const NpadControllerData& GetControllerFromHandle( | ||
| 185 | u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const; | ||
| 186 | NpadControllerData& GetControllerFromHandle( | ||
| 187 | u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 188 | const NpadControllerData& GetControllerFromHandle( | ||
| 189 | u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 190 | NpadControllerData& GetControllerFromNpadIdType(u64 aruid, Core::HID::NpadIdType npad_id); | ||
| 191 | const NpadControllerData& GetControllerFromNpadIdType(u64 aruid, | ||
| 192 | Core::HID::NpadIdType npad_id) const; | ||
| 193 | |||
| 194 | Core::HID::SixAxisSensorProperties& GetSixaxisProperties( | ||
| 195 | u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 196 | const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( | ||
| 197 | u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 198 | |||
| 199 | Core::HID::HIDCore& hid_core; | ||
| 200 | KernelHelpers::ServiceContext& service_context; | ||
| 201 | |||
| 202 | s32 ref_counter{}; | ||
| 203 | mutable std::mutex mutex; | ||
| 204 | NPadResource npad_resource; | ||
| 205 | AppletResourceHolder applet_resource_holder{}; | ||
| 206 | Kernel::KEvent* input_event{nullptr}; | ||
| 207 | std::mutex* input_mutex{nullptr}; | ||
| 208 | |||
| 209 | std::atomic<u64> press_state{}; | ||
| 210 | bool permit_vibration_session_enabled; | ||
| 211 | std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax> | ||
| 212 | controller_data{}; | ||
| 213 | }; | ||
| 214 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/npad/npad_data.cpp b/src/hid_core/resources/npad/npad_data.cpp new file mode 100644 index 000000000..c7e9760cb --- /dev/null +++ b/src/hid_core/resources/npad/npad_data.cpp | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "hid_core/hid_util.h" | ||
| 5 | #include "hid_core/resources/npad/npad_data.h" | ||
| 6 | |||
| 7 | namespace Service::HID { | ||
| 8 | |||
| 9 | NPadData::NPadData() { | ||
| 10 | ClearNpadSystemCommonPolicy(); | ||
| 11 | } | ||
| 12 | |||
| 13 | NPadData::~NPadData() = default; | ||
| 14 | |||
| 15 | NpadStatus NPadData::GetNpadStatus() const { | ||
| 16 | return status; | ||
| 17 | } | ||
| 18 | |||
| 19 | void NPadData::SetNpadAnalogStickUseCenterClamp(bool is_enabled) { | ||
| 20 | status.use_center_clamp.Assign(is_enabled); | ||
| 21 | } | ||
| 22 | |||
| 23 | bool NPadData::GetNpadAnalogStickUseCenterClamp() const { | ||
| 24 | return status.use_center_clamp.As<bool>(); | ||
| 25 | } | ||
| 26 | |||
| 27 | void NPadData::SetNpadSystemExtStateEnabled(bool is_enabled) { | ||
| 28 | status.system_ext_state.Assign(is_enabled); | ||
| 29 | } | ||
| 30 | |||
| 31 | bool NPadData::GetNpadSystemExtState() const { | ||
| 32 | return status.system_ext_state.As<bool>(); | ||
| 33 | } | ||
| 34 | |||
| 35 | Result NPadData::SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list) { | ||
| 36 | // Note: Real limit is 11. But array size is 10. N's bug? | ||
| 37 | if (list.size() > MaxSupportedNpadIdTypes) { | ||
| 38 | return ResultInvalidArraySize; | ||
| 39 | } | ||
| 40 | |||
| 41 | supported_npad_id_types_count = list.size(); | ||
| 42 | memcpy(supported_npad_id_types.data(), list.data(), | ||
| 43 | list.size() * sizeof(Core::HID::NpadIdType)); | ||
| 44 | |||
| 45 | return ResultSuccess; | ||
| 46 | } | ||
| 47 | |||
| 48 | std::size_t NPadData::GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const { | ||
| 49 | std::size_t out_size = std::min(supported_npad_id_types_count, out_list.size()); | ||
| 50 | |||
| 51 | memcpy(out_list.data(), supported_npad_id_types.data(), | ||
| 52 | out_size * sizeof(Core::HID::NpadIdType)); | ||
| 53 | |||
| 54 | return out_size; | ||
| 55 | } | ||
| 56 | |||
| 57 | bool NPadData::IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const { | ||
| 58 | for (std::size_t i = 0; i < supported_npad_id_types_count; i++) { | ||
| 59 | if (supported_npad_id_types[i] == npad_id) { | ||
| 60 | return true; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | return false; | ||
| 65 | } | ||
| 66 | |||
| 67 | void NPadData::SetNpadSystemCommonPolicy(bool is_full_policy) { | ||
| 68 | supported_npad_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::JoyDual | | ||
| 69 | Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System; | ||
| 70 | handheld_activation_mode = NpadHandheldActivationMode::Dual; | ||
| 71 | |||
| 72 | status.is_supported_styleset_set.Assign(true); | ||
| 73 | status.is_hold_type_set.Assign(true); | ||
| 74 | status.lr_assignment_mode.Assign(false); | ||
| 75 | status.is_policy.Assign(true); | ||
| 76 | if (is_full_policy) { | ||
| 77 | status.is_full_policy.Assign(true); | ||
| 78 | } | ||
| 79 | |||
| 80 | supported_npad_id_types_count = 10; | ||
| 81 | supported_npad_id_types[0] = Core::HID::NpadIdType::Player1; | ||
| 82 | supported_npad_id_types[1] = Core::HID::NpadIdType::Player2; | ||
| 83 | supported_npad_id_types[2] = Core::HID::NpadIdType::Player3; | ||
| 84 | supported_npad_id_types[3] = Core::HID::NpadIdType::Player4; | ||
| 85 | supported_npad_id_types[4] = Core::HID::NpadIdType::Player5; | ||
| 86 | supported_npad_id_types[5] = Core::HID::NpadIdType::Player6; | ||
| 87 | supported_npad_id_types[6] = Core::HID::NpadIdType::Player7; | ||
| 88 | supported_npad_id_types[7] = Core::HID::NpadIdType::Player8; | ||
| 89 | supported_npad_id_types[8] = Core::HID::NpadIdType::Other; | ||
| 90 | supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; | ||
| 91 | |||
| 92 | for (auto& input_protection : is_unintended_home_button_input_protection) { | ||
| 93 | input_protection = true; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | void NPadData::ClearNpadSystemCommonPolicy() { | ||
| 98 | status.raw = 0; | ||
| 99 | supported_npad_style_set = Core::HID::NpadStyleSet::All; | ||
| 100 | npad_hold_type = NpadJoyHoldType::Vertical; | ||
| 101 | handheld_activation_mode = NpadHandheldActivationMode::Dual; | ||
| 102 | |||
| 103 | for (auto& button_assignment : npad_button_assignment) { | ||
| 104 | button_assignment = Core::HID::NpadButton::None; | ||
| 105 | } | ||
| 106 | |||
| 107 | supported_npad_id_types_count = 10; | ||
| 108 | supported_npad_id_types[0] = Core::HID::NpadIdType::Player1; | ||
| 109 | supported_npad_id_types[1] = Core::HID::NpadIdType::Player2; | ||
| 110 | supported_npad_id_types[2] = Core::HID::NpadIdType::Player3; | ||
| 111 | supported_npad_id_types[3] = Core::HID::NpadIdType::Player4; | ||
| 112 | supported_npad_id_types[4] = Core::HID::NpadIdType::Player5; | ||
| 113 | supported_npad_id_types[5] = Core::HID::NpadIdType::Player6; | ||
| 114 | supported_npad_id_types[6] = Core::HID::NpadIdType::Player7; | ||
| 115 | supported_npad_id_types[7] = Core::HID::NpadIdType::Player8; | ||
| 116 | supported_npad_id_types[8] = Core::HID::NpadIdType::Other; | ||
| 117 | supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; | ||
| 118 | |||
| 119 | for (auto& input_protection : is_unintended_home_button_input_protection) { | ||
| 120 | input_protection = true; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | void NPadData::SetNpadJoyHoldType(NpadJoyHoldType hold_type) { | ||
| 125 | npad_hold_type = hold_type; | ||
| 126 | status.is_hold_type_set.Assign(true); | ||
| 127 | } | ||
| 128 | |||
| 129 | NpadJoyHoldType NPadData::GetNpadJoyHoldType() const { | ||
| 130 | return npad_hold_type; | ||
| 131 | } | ||
| 132 | |||
| 133 | void NPadData::SetHandheldActivationMode(NpadHandheldActivationMode activation_mode) { | ||
| 134 | handheld_activation_mode = activation_mode; | ||
| 135 | } | ||
| 136 | |||
| 137 | NpadHandheldActivationMode NPadData::GetHandheldActivationMode() const { | ||
| 138 | return handheld_activation_mode; | ||
| 139 | } | ||
| 140 | |||
| 141 | void NPadData::SetSupportedNpadStyleSet(Core::HID::NpadStyleSet style_set) { | ||
| 142 | supported_npad_style_set = style_set; | ||
| 143 | status.is_supported_styleset_set.Assign(true); | ||
| 144 | status.is_hold_type_set.Assign(true); | ||
| 145 | } | ||
| 146 | |||
| 147 | Core::HID::NpadStyleSet NPadData::GetSupportedNpadStyleSet() const { | ||
| 148 | return supported_npad_style_set; | ||
| 149 | } | ||
| 150 | |||
| 151 | bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const { | ||
| 152 | Core::HID::NpadStyleTag style = {supported_npad_style_set}; | ||
| 153 | switch (style_index) { | ||
| 154 | case Core::HID::NpadStyleIndex::ProController: | ||
| 155 | return style.fullkey.As<bool>(); | ||
| 156 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 157 | return style.handheld.As<bool>(); | ||
| 158 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 159 | return style.joycon_dual.As<bool>(); | ||
| 160 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 161 | return style.joycon_left.As<bool>(); | ||
| 162 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 163 | return style.joycon_right.As<bool>(); | ||
| 164 | case Core::HID::NpadStyleIndex::GameCube: | ||
| 165 | return style.gamecube.As<bool>(); | ||
| 166 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 167 | return style.palma.As<bool>(); | ||
| 168 | case Core::HID::NpadStyleIndex::NES: | ||
| 169 | return style.lark.As<bool>(); | ||
| 170 | case Core::HID::NpadStyleIndex::SNES: | ||
| 171 | return style.lucia.As<bool>(); | ||
| 172 | case Core::HID::NpadStyleIndex::N64: | ||
| 173 | return style.lagoon.As<bool>(); | ||
| 174 | case Core::HID::NpadStyleIndex::SegaGenesis: | ||
| 175 | return style.lager.As<bool>(); | ||
| 176 | default: | ||
| 177 | return false; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | void NPadData::SetLrAssignmentMode(bool is_enabled) { | ||
| 182 | status.lr_assignment_mode.Assign(is_enabled); | ||
| 183 | } | ||
| 184 | |||
| 185 | bool NPadData::GetLrAssignmentMode() const { | ||
| 186 | return status.lr_assignment_mode.As<bool>(); | ||
| 187 | } | ||
| 188 | |||
| 189 | void NPadData::SetAssigningSingleOnSlSrPress(bool is_enabled) { | ||
| 190 | status.assigning_single_on_sl_sr_press.Assign(is_enabled); | ||
| 191 | } | ||
| 192 | |||
| 193 | bool NPadData::GetAssigningSingleOnSlSrPress() const { | ||
| 194 | return status.assigning_single_on_sl_sr_press.As<bool>(); | ||
| 195 | } | ||
| 196 | |||
| 197 | void NPadData::SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id) { | ||
| 198 | is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)] = is_enabled; | ||
| 199 | } | ||
| 200 | |||
| 201 | bool NPadData::GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const { | ||
| 202 | return is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)]; | ||
| 203 | } | ||
| 204 | |||
| 205 | void NPadData::SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment, | ||
| 206 | std::size_t style_index) { | ||
| 207 | npad_button_assignment[style_index] = button_assignment; | ||
| 208 | } | ||
| 209 | |||
| 210 | Core::HID::NpadButton NPadData::GetCaptureButtonAssignment(std::size_t style_index) const { | ||
| 211 | return npad_button_assignment[style_index]; | ||
| 212 | } | ||
| 213 | |||
| 214 | std::size_t NPadData::GetNpadCaptureButtonAssignmentList( | ||
| 215 | std::span<Core::HID::NpadButton> out_list) const { | ||
| 216 | for (std::size_t i = 0; i < out_list.size(); i++) { | ||
| 217 | Core::HID::NpadStyleSet style_set = GetStylesetByIndex(i); | ||
| 218 | if ((style_set & supported_npad_style_set) == Core::HID::NpadStyleSet::None || | ||
| 219 | npad_button_assignment[i] == Core::HID::NpadButton::None) { | ||
| 220 | return i; | ||
| 221 | } | ||
| 222 | out_list[i] = npad_button_assignment[i]; | ||
| 223 | } | ||
| 224 | |||
| 225 | return out_list.size(); | ||
| 226 | } | ||
| 227 | |||
| 228 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/npad/npad_data.h b/src/hid_core/resources/npad/npad_data.h new file mode 100644 index 000000000..86bd3b81c --- /dev/null +++ b/src/hid_core/resources/npad/npad_data.h | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <span> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/hle/result.h" | ||
| 11 | #include "hid_core/hid_types.h" | ||
| 12 | #include "hid_core/resources/npad/npad_types.h" | ||
| 13 | |||
| 14 | namespace Service::HID { | ||
| 15 | |||
| 16 | struct NpadStatus { | ||
| 17 | union { | ||
| 18 | u32 raw{}; | ||
| 19 | |||
| 20 | BitField<0, 1, u32> is_supported_styleset_set; | ||
| 21 | BitField<1, 1, u32> is_hold_type_set; | ||
| 22 | BitField<2, 1, u32> lr_assignment_mode; | ||
| 23 | BitField<3, 1, u32> assigning_single_on_sl_sr_press; | ||
| 24 | BitField<4, 1, u32> is_full_policy; | ||
| 25 | BitField<5, 1, u32> is_policy; | ||
| 26 | BitField<6, 1, u32> use_center_clamp; | ||
| 27 | BitField<7, 1, u32> system_ext_state; | ||
| 28 | }; | ||
| 29 | }; | ||
| 30 | static_assert(sizeof(NpadStatus) == 4, "NpadStatus is an invalid size"); | ||
| 31 | |||
| 32 | /// Handles Npad request from HID interfaces | ||
| 33 | class NPadData final { | ||
| 34 | public: | ||
| 35 | explicit NPadData(); | ||
| 36 | ~NPadData(); | ||
| 37 | |||
| 38 | NpadStatus GetNpadStatus() const; | ||
| 39 | |||
| 40 | void SetNpadAnalogStickUseCenterClamp(bool is_enabled); | ||
| 41 | bool GetNpadAnalogStickUseCenterClamp() const; | ||
| 42 | |||
| 43 | void SetNpadSystemExtStateEnabled(bool is_enabled); | ||
| 44 | bool GetNpadSystemExtState() const; | ||
| 45 | |||
| 46 | Result SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list); | ||
| 47 | std::size_t GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const; | ||
| 48 | bool IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const; | ||
| 49 | |||
| 50 | void SetNpadSystemCommonPolicy(bool is_full_policy); | ||
| 51 | void ClearNpadSystemCommonPolicy(); | ||
| 52 | |||
| 53 | void SetNpadJoyHoldType(NpadJoyHoldType hold_type); | ||
| 54 | NpadJoyHoldType GetNpadJoyHoldType() const; | ||
| 55 | |||
| 56 | void SetHandheldActivationMode(NpadHandheldActivationMode activation_mode); | ||
| 57 | NpadHandheldActivationMode GetHandheldActivationMode() const; | ||
| 58 | |||
| 59 | void SetSupportedNpadStyleSet(Core::HID::NpadStyleSet style_set); | ||
| 60 | Core::HID::NpadStyleSet GetSupportedNpadStyleSet() const; | ||
| 61 | bool IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const; | ||
| 62 | |||
| 63 | void SetLrAssignmentMode(bool is_enabled); | ||
| 64 | bool GetLrAssignmentMode() const; | ||
| 65 | |||
| 66 | void SetAssigningSingleOnSlSrPress(bool is_enabled); | ||
| 67 | bool GetAssigningSingleOnSlSrPress() const; | ||
| 68 | |||
| 69 | void SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id); | ||
| 70 | bool GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const; | ||
| 71 | |||
| 72 | void SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment, | ||
| 73 | std::size_t style_index); | ||
| 74 | Core::HID::NpadButton GetCaptureButtonAssignment(std::size_t style_index) const; | ||
| 75 | std::size_t GetNpadCaptureButtonAssignmentList(std::span<Core::HID::NpadButton> out_list) const; | ||
| 76 | |||
| 77 | private: | ||
| 78 | NpadStatus status{}; | ||
| 79 | Core::HID::NpadStyleSet supported_npad_style_set{Core::HID::NpadStyleSet::All}; | ||
| 80 | NpadJoyHoldType npad_hold_type{NpadJoyHoldType::Vertical}; | ||
| 81 | NpadHandheldActivationMode handheld_activation_mode{}; | ||
| 82 | std::array<Core::HID::NpadIdType, MaxSupportedNpadIdTypes> supported_npad_id_types{}; | ||
| 83 | std::array<Core::HID::NpadButton, StyleIndexCount> npad_button_assignment{}; | ||
| 84 | std::size_t supported_npad_id_types_count{}; | ||
| 85 | std::array<bool, MaxSupportedNpadIdTypes> is_unintended_home_button_input_protection{}; | ||
| 86 | }; | ||
| 87 | |||
| 88 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/npad/npad_resource.cpp b/src/hid_core/resources/npad/npad_resource.cpp new file mode 100644 index 000000000..b0255a05c --- /dev/null +++ b/src/hid_core/resources/npad/npad_resource.cpp | |||
| @@ -0,0 +1,685 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/k_event.h" | ||
| 5 | #include "core/hle/kernel/k_readable_event.h" | ||
| 6 | #include "hid_core/hid_result.h" | ||
| 7 | #include "hid_core/hid_util.h" | ||
| 8 | #include "hid_core/resources/npad/npad_resource.h" | ||
| 9 | #include "hid_core/resources/npad/npad_types.h" | ||
| 10 | |||
| 11 | namespace Service::HID { | ||
| 12 | |||
| 13 | NPadResource::NPadResource(KernelHelpers::ServiceContext& context) : service_context{context} {} | ||
| 14 | |||
| 15 | NPadResource::~NPadResource() = default; | ||
| 16 | |||
| 17 | Result NPadResource::RegisterAppletResourceUserId(u64 aruid) { | ||
| 18 | const auto aruid_index = GetIndexFromAruid(aruid); | ||
| 19 | if (aruid_index < AruidIndexMax) { | ||
| 20 | return ResultAruidAlreadyRegistered; | ||
| 21 | } | ||
| 22 | |||
| 23 | std::size_t data_index = AruidIndexMax; | ||
| 24 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 25 | if (!state[i].flag.is_initialized) { | ||
| 26 | data_index = i; | ||
| 27 | break; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | if (data_index == AruidIndexMax) { | ||
| 32 | return ResultAruidNoAvailableEntries; | ||
| 33 | } | ||
| 34 | |||
| 35 | auto& aruid_data = state[data_index]; | ||
| 36 | |||
| 37 | aruid_data.aruid = aruid; | ||
| 38 | aruid_data.flag.is_initialized.Assign(true); | ||
| 39 | |||
| 40 | data_index = AruidIndexMax; | ||
| 41 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 42 | if (registration_list.flag[i] == RegistrationStatus::Initialized) { | ||
| 43 | if (registration_list.aruid[i] != aruid) { | ||
| 44 | continue; | ||
| 45 | } | ||
| 46 | data_index = i; | ||
| 47 | break; | ||
| 48 | } | ||
| 49 | if (registration_list.flag[i] == RegistrationStatus::None) { | ||
| 50 | data_index = i; | ||
| 51 | break; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | if (data_index == AruidIndexMax) { | ||
| 56 | return ResultSuccess; | ||
| 57 | } | ||
| 58 | |||
| 59 | registration_list.flag[data_index] = RegistrationStatus::Initialized; | ||
| 60 | registration_list.aruid[data_index] = aruid; | ||
| 61 | |||
| 62 | return ResultSuccess; | ||
| 63 | } | ||
| 64 | |||
| 65 | void NPadResource::UnregisterAppletResourceUserId(u64 aruid) { | ||
| 66 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 67 | |||
| 68 | DestroyStyleSetUpdateEvents(aruid); | ||
| 69 | if (aruid_index < AruidIndexMax) { | ||
| 70 | state[aruid_index] = {}; | ||
| 71 | registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) { | ||
| 76 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 77 | |||
| 78 | if (aruid_index >= AruidIndexMax) { | ||
| 79 | return; | ||
| 80 | } | ||
| 81 | |||
| 82 | for (auto& controller_state : state[aruid_index].controller_state) { | ||
| 83 | if (!controller_state.is_styleset_update_event_initialized) { | ||
| 84 | continue; | ||
| 85 | } | ||
| 86 | service_context.CloseEvent(controller_state.style_set_update_event); | ||
| 87 | controller_state.is_styleset_update_event_initialized = false; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | Result NPadResource::Activate(u64 aruid) { | ||
| 92 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 93 | |||
| 94 | if (aruid_index >= AruidIndexMax) { | ||
| 95 | return ResultSuccess; | ||
| 96 | } | ||
| 97 | |||
| 98 | auto& state_data = state[aruid_index]; | ||
| 99 | |||
| 100 | if (state_data.flag.is_assigned) { | ||
| 101 | return ResultAruidAlreadyRegistered; | ||
| 102 | } | ||
| 103 | |||
| 104 | state_data.flag.is_assigned.Assign(true); | ||
| 105 | state_data.data.ClearNpadSystemCommonPolicy(); | ||
| 106 | state_data.npad_revision = NpadRevision::Revision0; | ||
| 107 | state_data.button_config = {}; | ||
| 108 | |||
| 109 | if (active_data_aruid == aruid) { | ||
| 110 | default_hold_type = active_data.GetNpadJoyHoldType(); | ||
| 111 | active_data.SetNpadJoyHoldType(default_hold_type); | ||
| 112 | } | ||
| 113 | return ResultSuccess; | ||
| 114 | } | ||
| 115 | |||
| 116 | Result NPadResource::Activate() { | ||
| 117 | if (ref_counter == std::numeric_limits<s32>::max() - 1) { | ||
| 118 | return ResultAppletResourceOverflow; | ||
| 119 | } | ||
| 120 | if (ref_counter == 0) { | ||
| 121 | RegisterAppletResourceUserId(SystemAruid); | ||
| 122 | Activate(SystemAruid); | ||
| 123 | } | ||
| 124 | ref_counter++; | ||
| 125 | return ResultSuccess; | ||
| 126 | } | ||
| 127 | |||
| 128 | Result NPadResource::Deactivate() { | ||
| 129 | if (ref_counter == 0) { | ||
| 130 | return ResultAppletResourceNotInitialized; | ||
| 131 | } | ||
| 132 | |||
| 133 | UnregisterAppletResourceUserId(SystemAruid); | ||
| 134 | ref_counter--; | ||
| 135 | return ResultSuccess; | ||
| 136 | } | ||
| 137 | |||
| 138 | NPadData* NPadResource::GetActiveData() { | ||
| 139 | return &active_data; | ||
| 140 | } | ||
| 141 | |||
| 142 | u64 NPadResource::GetActiveDataAruid() { | ||
| 143 | return active_data_aruid; | ||
| 144 | } | ||
| 145 | |||
| 146 | void NPadResource::SetAppletResourceUserId(u64 aruid) { | ||
| 147 | if (active_data_aruid == aruid) { | ||
| 148 | return; | ||
| 149 | } | ||
| 150 | |||
| 151 | active_data_aruid = aruid; | ||
| 152 | default_hold_type = active_data.GetNpadJoyHoldType(); | ||
| 153 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 154 | |||
| 155 | if (aruid_index >= AruidIndexMax) { | ||
| 156 | return; | ||
| 157 | } | ||
| 158 | |||
| 159 | auto& data = state[aruid_index].data; | ||
| 160 | if (data.GetNpadStatus().is_policy || data.GetNpadStatus().is_full_policy) { | ||
| 161 | data.SetNpadJoyHoldType(default_hold_type); | ||
| 162 | } | ||
| 163 | |||
| 164 | active_data = data; | ||
| 165 | if (data.GetNpadStatus().is_hold_type_set) { | ||
| 166 | active_data.SetNpadJoyHoldType(default_hold_type); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | std::size_t NPadResource::GetIndexFromAruid(u64 aruid) const { | ||
| 171 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 172 | if (registration_list.flag[i] == RegistrationStatus::Initialized && | ||
| 173 | registration_list.aruid[i] == aruid) { | ||
| 174 | return i; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | return AruidIndexMax; | ||
| 178 | } | ||
| 179 | |||
| 180 | Result NPadResource::ApplyNpadSystemCommonPolicy(u64 aruid, bool is_full_policy) { | ||
| 181 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 182 | if (aruid_index >= AruidIndexMax) { | ||
| 183 | return ResultNpadNotConnected; | ||
| 184 | } | ||
| 185 | |||
| 186 | auto& data = state[aruid_index].data; | ||
| 187 | data.SetNpadSystemCommonPolicy(is_full_policy); | ||
| 188 | data.SetNpadJoyHoldType(default_hold_type); | ||
| 189 | if (active_data_aruid == aruid) { | ||
| 190 | active_data.SetNpadSystemCommonPolicy(is_full_policy); | ||
| 191 | active_data.SetNpadJoyHoldType(default_hold_type); | ||
| 192 | } | ||
| 193 | return ResultSuccess; | ||
| 194 | } | ||
| 195 | |||
| 196 | Result NPadResource::ClearNpadSystemCommonPolicy(u64 aruid) { | ||
| 197 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 198 | if (aruid_index >= AruidIndexMax) { | ||
| 199 | return ResultNpadNotConnected; | ||
| 200 | } | ||
| 201 | |||
| 202 | state[aruid_index].data.ClearNpadSystemCommonPolicy(); | ||
| 203 | if (active_data_aruid == aruid) { | ||
| 204 | active_data.ClearNpadSystemCommonPolicy(); | ||
| 205 | } | ||
| 206 | return ResultSuccess; | ||
| 207 | } | ||
| 208 | |||
| 209 | Result NPadResource::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet style_set) { | ||
| 210 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 211 | if (aruid_index >= AruidIndexMax) { | ||
| 212 | return ResultNpadNotConnected; | ||
| 213 | } | ||
| 214 | |||
| 215 | auto& data = state[aruid_index].data; | ||
| 216 | data.SetSupportedNpadStyleSet(style_set); | ||
| 217 | if (active_data_aruid == aruid) { | ||
| 218 | active_data.SetSupportedNpadStyleSet(style_set); | ||
| 219 | active_data.SetNpadJoyHoldType(data.GetNpadJoyHoldType()); | ||
| 220 | } | ||
| 221 | return ResultSuccess; | ||
| 222 | } | ||
| 223 | |||
| 224 | Result NPadResource::GetSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_Set, | ||
| 225 | u64 aruid) const { | ||
| 226 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 227 | if (aruid_index >= AruidIndexMax) { | ||
| 228 | return ResultNpadNotConnected; | ||
| 229 | } | ||
| 230 | |||
| 231 | auto& data = state[aruid_index].data; | ||
| 232 | if (!data.GetNpadStatus().is_supported_styleset_set) { | ||
| 233 | return ResultUndefinedStyleset; | ||
| 234 | } | ||
| 235 | |||
| 236 | out_style_Set = data.GetSupportedNpadStyleSet(); | ||
| 237 | return ResultSuccess; | ||
| 238 | } | ||
| 239 | |||
| 240 | Result NPadResource::GetMaskedSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_set, | ||
| 241 | u64 aruid) const { | ||
| 242 | if (aruid == SystemAruid) { | ||
| 243 | out_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 244 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 245 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Palma | | ||
| 246 | Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System; | ||
| 247 | return ResultSuccess; | ||
| 248 | } | ||
| 249 | |||
| 250 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 251 | if (aruid_index >= AruidIndexMax) { | ||
| 252 | return ResultNpadNotConnected; | ||
| 253 | } | ||
| 254 | |||
| 255 | auto& data = state[aruid_index].data; | ||
| 256 | if (!data.GetNpadStatus().is_supported_styleset_set) { | ||
| 257 | return ResultUndefinedStyleset; | ||
| 258 | } | ||
| 259 | |||
| 260 | Core::HID::NpadStyleSet mask{Core::HID::NpadStyleSet::None}; | ||
| 261 | out_style_set = data.GetSupportedNpadStyleSet(); | ||
| 262 | |||
| 263 | switch (state[aruid_index].npad_revision) { | ||
| 264 | case NpadRevision::Revision1: | ||
| 265 | mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 266 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 267 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc | | ||
| 268 | Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::SystemExt | | ||
| 269 | Core::HID::NpadStyleSet::System; | ||
| 270 | break; | ||
| 271 | case NpadRevision::Revision2: | ||
| 272 | mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 273 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 274 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc | | ||
| 275 | Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark | | ||
| 276 | Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System; | ||
| 277 | break; | ||
| 278 | case NpadRevision::Revision3: | ||
| 279 | mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 280 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 281 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc | | ||
| 282 | Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark | | ||
| 283 | Core::HID::NpadStyleSet::HandheldLark | Core::HID::NpadStyleSet::Lucia | | ||
| 284 | Core::HID::NpadStyleSet::Lagoon | Core::HID::NpadStyleSet::Lager | | ||
| 285 | Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System; | ||
| 286 | break; | ||
| 287 | default: | ||
| 288 | mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 289 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 290 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::SystemExt | | ||
| 291 | Core::HID::NpadStyleSet::System; | ||
| 292 | break; | ||
| 293 | } | ||
| 294 | |||
| 295 | out_style_set = out_style_set & mask; | ||
| 296 | return ResultSuccess; | ||
| 297 | } | ||
| 298 | |||
| 299 | Result NPadResource::GetAvailableStyleset(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const { | ||
| 300 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 301 | if (aruid_index >= AruidIndexMax) { | ||
| 302 | return ResultNpadNotConnected; | ||
| 303 | } | ||
| 304 | |||
| 305 | auto& data = state[aruid_index].data; | ||
| 306 | if (!data.GetNpadStatus().is_supported_styleset_set) { | ||
| 307 | return ResultUndefinedStyleset; | ||
| 308 | } | ||
| 309 | |||
| 310 | Core::HID::NpadStyleSet mask{Core::HID::NpadStyleSet::None}; | ||
| 311 | out_style_set = data.GetSupportedNpadStyleSet(); | ||
| 312 | |||
| 313 | switch (state[aruid_index].npad_revision) { | ||
| 314 | case NpadRevision::Revision1: | ||
| 315 | mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 316 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 317 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc | | ||
| 318 | Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::SystemExt | | ||
| 319 | Core::HID::NpadStyleSet::System; | ||
| 320 | break; | ||
| 321 | case NpadRevision::Revision2: | ||
| 322 | mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 323 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 324 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc | | ||
| 325 | Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark | | ||
| 326 | Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System; | ||
| 327 | break; | ||
| 328 | case NpadRevision::Revision3: | ||
| 329 | mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 330 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 331 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc | | ||
| 332 | Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark | | ||
| 333 | Core::HID::NpadStyleSet::HandheldLark | Core::HID::NpadStyleSet::Lucia | | ||
| 334 | Core::HID::NpadStyleSet::Lagoon | Core::HID::NpadStyleSet::Lager | | ||
| 335 | Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System; | ||
| 336 | break; | ||
| 337 | default: | ||
| 338 | mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld | | ||
| 339 | Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft | | ||
| 340 | Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::SystemExt | | ||
| 341 | Core::HID::NpadStyleSet::System; | ||
| 342 | break; | ||
| 343 | } | ||
| 344 | |||
| 345 | out_style_set = out_style_set & mask; | ||
| 346 | return ResultSuccess; | ||
| 347 | } | ||
| 348 | |||
| 349 | NpadRevision NPadResource::GetNpadRevision(u64 aruid) const { | ||
| 350 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 351 | if (aruid_index >= AruidIndexMax) { | ||
| 352 | return NpadRevision::Revision0; | ||
| 353 | } | ||
| 354 | |||
| 355 | return state[aruid_index].npad_revision; | ||
| 356 | } | ||
| 357 | |||
| 358 | Result NPadResource::IsSupportedNpadStyleSet(bool& is_set, u64 aruid) { | ||
| 359 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 360 | if (aruid_index >= AruidIndexMax) { | ||
| 361 | return ResultNpadNotConnected; | ||
| 362 | } | ||
| 363 | |||
| 364 | is_set = state[aruid_index].data.GetNpadStatus().is_supported_styleset_set.Value() != 0; | ||
| 365 | return ResultSuccess; | ||
| 366 | } | ||
| 367 | |||
| 368 | Result NPadResource::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) { | ||
| 369 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 370 | if (aruid_index >= AruidIndexMax) { | ||
| 371 | return ResultNpadNotConnected; | ||
| 372 | } | ||
| 373 | |||
| 374 | state[aruid_index].data.SetNpadJoyHoldType(hold_type); | ||
| 375 | if (active_data_aruid == aruid) { | ||
| 376 | active_data.SetNpadJoyHoldType(hold_type); | ||
| 377 | } | ||
| 378 | return ResultSuccess; | ||
| 379 | } | ||
| 380 | |||
| 381 | Result NPadResource::GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const { | ||
| 382 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 383 | if (aruid_index >= AruidIndexMax) { | ||
| 384 | return ResultNpadNotConnected; | ||
| 385 | } | ||
| 386 | |||
| 387 | auto& data = state[aruid_index].data; | ||
| 388 | if (data.GetNpadStatus().is_policy || data.GetNpadStatus().is_full_policy) { | ||
| 389 | hold_type = active_data.GetNpadJoyHoldType(); | ||
| 390 | return ResultSuccess; | ||
| 391 | } | ||
| 392 | hold_type = data.GetNpadJoyHoldType(); | ||
| 393 | return ResultSuccess; | ||
| 394 | } | ||
| 395 | |||
| 396 | Result NPadResource::SetNpadHandheldActivationMode(u64 aruid, | ||
| 397 | NpadHandheldActivationMode activation_mode) { | ||
| 398 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 399 | if (aruid_index >= AruidIndexMax) { | ||
| 400 | return ResultNpadNotConnected; | ||
| 401 | } | ||
| 402 | |||
| 403 | state[aruid_index].data.SetHandheldActivationMode(activation_mode); | ||
| 404 | if (active_data_aruid == aruid) { | ||
| 405 | active_data.SetHandheldActivationMode(activation_mode); | ||
| 406 | } | ||
| 407 | return ResultSuccess; | ||
| 408 | } | ||
| 409 | |||
| 410 | Result NPadResource::GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode, | ||
| 411 | u64 aruid) const { | ||
| 412 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 413 | if (aruid_index >= AruidIndexMax) { | ||
| 414 | return ResultNpadNotConnected; | ||
| 415 | } | ||
| 416 | |||
| 417 | activation_mode = state[aruid_index].data.GetHandheldActivationMode(); | ||
| 418 | return ResultSuccess; | ||
| 419 | } | ||
| 420 | |||
| 421 | Result NPadResource::SetSupportedNpadIdType( | ||
| 422 | u64 aruid, std::span<const Core::HID::NpadIdType> supported_npad_list) { | ||
| 423 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 424 | if (aruid_index >= AruidIndexMax) { | ||
| 425 | return ResultNpadNotConnected; | ||
| 426 | } | ||
| 427 | if (supported_npad_list.size() > MaxSupportedNpadIdTypes) { | ||
| 428 | return ResultInvalidArraySize; | ||
| 429 | } | ||
| 430 | |||
| 431 | Result result = state[aruid_index].data.SetSupportedNpadIdType(supported_npad_list); | ||
| 432 | if (result.IsSuccess() && active_data_aruid == aruid) { | ||
| 433 | result = active_data.SetSupportedNpadIdType(supported_npad_list); | ||
| 434 | } | ||
| 435 | |||
| 436 | return result; | ||
| 437 | } | ||
| 438 | |||
| 439 | bool NPadResource::IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const { | ||
| 440 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 441 | if (aruid_index >= AruidIndexMax) { | ||
| 442 | return false; | ||
| 443 | } | ||
| 444 | return state[aruid_index].data.IsNpadStyleIndexSupported(style_index); | ||
| 445 | } | ||
| 446 | |||
| 447 | Result NPadResource::SetLrAssignmentMode(u64 aruid, bool is_enabled) { | ||
| 448 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 449 | if (aruid_index >= AruidIndexMax) { | ||
| 450 | return ResultNpadNotConnected; | ||
| 451 | } | ||
| 452 | |||
| 453 | state[aruid_index].data.SetLrAssignmentMode(is_enabled); | ||
| 454 | if (active_data_aruid == aruid) { | ||
| 455 | active_data.SetLrAssignmentMode(is_enabled); | ||
| 456 | } | ||
| 457 | return ResultSuccess; | ||
| 458 | } | ||
| 459 | |||
| 460 | Result NPadResource::GetLrAssignmentMode(bool& is_enabled, u64 aruid) const { | ||
| 461 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 462 | if (aruid_index >= AruidIndexMax) { | ||
| 463 | return ResultNpadNotConnected; | ||
| 464 | } | ||
| 465 | |||
| 466 | is_enabled = state[aruid_index].data.GetLrAssignmentMode(); | ||
| 467 | return ResultSuccess; | ||
| 468 | } | ||
| 469 | |||
| 470 | Result NPadResource::SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled) { | ||
| 471 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 472 | if (aruid_index >= AruidIndexMax) { | ||
| 473 | return ResultNpadNotConnected; | ||
| 474 | } | ||
| 475 | |||
| 476 | state[aruid_index].data.SetAssigningSingleOnSlSrPress(is_enabled); | ||
| 477 | if (active_data_aruid == aruid) { | ||
| 478 | active_data.SetAssigningSingleOnSlSrPress(is_enabled); | ||
| 479 | } | ||
| 480 | return ResultSuccess; | ||
| 481 | } | ||
| 482 | |||
| 483 | Result NPadResource::IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const { | ||
| 484 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 485 | if (aruid_index >= AruidIndexMax) { | ||
| 486 | return ResultNpadNotConnected; | ||
| 487 | } | ||
| 488 | |||
| 489 | is_enabled = state[aruid_index].data.GetAssigningSingleOnSlSrPress(); | ||
| 490 | return ResultSuccess; | ||
| 491 | } | ||
| 492 | |||
| 493 | Result NPadResource::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, | ||
| 494 | Kernel::KReadableEvent** out_event, | ||
| 495 | Core::HID::NpadIdType npad_id) { | ||
| 496 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 497 | if (aruid_index >= AruidIndexMax) { | ||
| 498 | return ResultNpadNotConnected; | ||
| 499 | } | ||
| 500 | |||
| 501 | auto& controller_state = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)]; | ||
| 502 | if (!controller_state.is_styleset_update_event_initialized) { | ||
| 503 | // Auto clear = true | ||
| 504 | controller_state.style_set_update_event = | ||
| 505 | service_context.CreateEvent("NpadResource:StylesetUpdateEvent"); | ||
| 506 | |||
| 507 | // Assume creating the event succeeds otherwise crash the system here | ||
| 508 | controller_state.is_styleset_update_event_initialized = true; | ||
| 509 | } | ||
| 510 | |||
| 511 | *out_event = &controller_state.style_set_update_event->GetReadableEvent(); | ||
| 512 | |||
| 513 | if (controller_state.is_styleset_update_event_initialized) { | ||
| 514 | controller_state.style_set_update_event->Signal(); | ||
| 515 | } | ||
| 516 | |||
| 517 | return ResultSuccess; | ||
| 518 | } | ||
| 519 | |||
| 520 | Result NPadResource::SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id) { | ||
| 521 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 522 | if (aruid_index >= AruidIndexMax) { | ||
| 523 | return ResultNpadNotConnected; | ||
| 524 | } | ||
| 525 | auto controller = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)]; | ||
| 526 | if (controller.is_styleset_update_event_initialized) { | ||
| 527 | controller.style_set_update_event->Signal(); | ||
| 528 | } | ||
| 529 | return ResultSuccess; | ||
| 530 | } | ||
| 531 | |||
| 532 | Result NPadResource::GetHomeProtectionEnabled(bool& is_enabled, u64 aruid, | ||
| 533 | Core::HID::NpadIdType npad_id) const { | ||
| 534 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 535 | if (aruid_index >= AruidIndexMax) { | ||
| 536 | return ResultNpadNotConnected; | ||
| 537 | } | ||
| 538 | |||
| 539 | is_enabled = state[aruid_index].data.GetHomeProtectionEnabled(npad_id); | ||
| 540 | return ResultSuccess; | ||
| 541 | } | ||
| 542 | |||
| 543 | Result NPadResource::SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 544 | bool is_enabled) { | ||
| 545 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 546 | if (aruid_index >= AruidIndexMax) { | ||
| 547 | return ResultNpadNotConnected; | ||
| 548 | } | ||
| 549 | |||
| 550 | state[aruid_index].data.SetHomeProtectionEnabled(is_enabled, npad_id); | ||
| 551 | if (active_data_aruid == aruid) { | ||
| 552 | active_data.SetHomeProtectionEnabled(is_enabled, npad_id); | ||
| 553 | } | ||
| 554 | return ResultSuccess; | ||
| 555 | } | ||
| 556 | |||
| 557 | Result NPadResource::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) { | ||
| 558 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 559 | if (aruid_index >= AruidIndexMax) { | ||
| 560 | return ResultNpadNotConnected; | ||
| 561 | } | ||
| 562 | |||
| 563 | state[aruid_index].data.SetNpadAnalogStickUseCenterClamp(is_enabled); | ||
| 564 | if (active_data_aruid == aruid) { | ||
| 565 | active_data.SetNpadAnalogStickUseCenterClamp(is_enabled); | ||
| 566 | } | ||
| 567 | return ResultSuccess; | ||
| 568 | } | ||
| 569 | |||
| 570 | Result NPadResource::SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index, | ||
| 571 | Core::HID::NpadButton button_config) { | ||
| 572 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 573 | if (aruid_index >= AruidIndexMax) { | ||
| 574 | return ResultNpadNotConnected; | ||
| 575 | } | ||
| 576 | |||
| 577 | state[aruid_index].button_config[NpadIdTypeToIndex(npad_id)][index] = button_config; | ||
| 578 | return ResultSuccess; | ||
| 579 | } | ||
| 580 | |||
| 581 | Core::HID::NpadButton NPadResource::GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 582 | std::size_t index, Core::HID::NpadButton mask, | ||
| 583 | bool is_enabled) { | ||
| 584 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 585 | if (aruid_index >= AruidIndexMax) { | ||
| 586 | return Core::HID::NpadButton::None; | ||
| 587 | } | ||
| 588 | |||
| 589 | auto& button_config = state[aruid_index].button_config[NpadIdTypeToIndex(npad_id)][index]; | ||
| 590 | if (is_enabled) { | ||
| 591 | button_config = button_config | mask; | ||
| 592 | return button_config; | ||
| 593 | } | ||
| 594 | |||
| 595 | button_config = Core::HID::NpadButton::None; | ||
| 596 | return Core::HID::NpadButton::None; | ||
| 597 | } | ||
| 598 | |||
| 599 | void NPadResource::ResetButtonConfig() { | ||
| 600 | for (auto& selected_state : state) { | ||
| 601 | selected_state.button_config = {}; | ||
| 602 | } | ||
| 603 | } | ||
| 604 | |||
| 605 | Result NPadResource::SetNpadCaptureButtonAssignment(u64 aruid, | ||
| 606 | Core::HID::NpadStyleSet npad_style_set, | ||
| 607 | Core::HID::NpadButton button_assignment) { | ||
| 608 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 609 | if (aruid_index >= AruidIndexMax) { | ||
| 610 | return ResultNpadNotConnected; | ||
| 611 | } | ||
| 612 | |||
| 613 | // Must be a power of two | ||
| 614 | const auto raw_styleset = static_cast<u32>(npad_style_set); | ||
| 615 | if (raw_styleset == 0 && (raw_styleset & (raw_styleset - 1)) != 0) { | ||
| 616 | return ResultMultipleStyleSetSelected; | ||
| 617 | } | ||
| 618 | |||
| 619 | std::size_t style_index{}; | ||
| 620 | Core::HID::NpadStyleSet style_selected{}; | ||
| 621 | for (style_index = 0; style_index < StyleIndexCount; ++style_index) { | ||
| 622 | style_selected = GetStylesetByIndex(style_index); | ||
| 623 | if (npad_style_set == style_selected) { | ||
| 624 | break; | ||
| 625 | } | ||
| 626 | } | ||
| 627 | |||
| 628 | if (style_selected == Core::HID::NpadStyleSet::None) { | ||
| 629 | return ResultMultipleStyleSetSelected; | ||
| 630 | } | ||
| 631 | |||
| 632 | state[aruid_index].data.SetCaptureButtonAssignment(button_assignment, style_index); | ||
| 633 | if (active_data_aruid == aruid) { | ||
| 634 | active_data.SetCaptureButtonAssignment(button_assignment, style_index); | ||
| 635 | } | ||
| 636 | return ResultSuccess; | ||
| 637 | } | ||
| 638 | |||
| 639 | Result NPadResource::ClearNpadCaptureButtonAssignment(u64 aruid) { | ||
| 640 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 641 | if (aruid_index >= AruidIndexMax) { | ||
| 642 | return ResultNpadNotConnected; | ||
| 643 | } | ||
| 644 | |||
| 645 | for (std::size_t i = 0; i < StyleIndexCount; i++) { | ||
| 646 | state[aruid_index].data.SetCaptureButtonAssignment(Core::HID::NpadButton::None, i); | ||
| 647 | if (active_data_aruid == aruid) { | ||
| 648 | active_data.SetCaptureButtonAssignment(Core::HID::NpadButton::None, i); | ||
| 649 | } | ||
| 650 | } | ||
| 651 | return ResultSuccess; | ||
| 652 | } | ||
| 653 | |||
| 654 | std::size_t NPadResource::GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list, | ||
| 655 | u64 aruid) const { | ||
| 656 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 657 | if (aruid_index >= AruidIndexMax) { | ||
| 658 | return 0; | ||
| 659 | } | ||
| 660 | return state[aruid_index].data.GetNpadCaptureButtonAssignmentList(out_list); | ||
| 661 | } | ||
| 662 | |||
| 663 | void NPadResource::SetNpadRevision(u64 aruid, NpadRevision revision) { | ||
| 664 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 665 | if (aruid_index >= AruidIndexMax) { | ||
| 666 | return; | ||
| 667 | } | ||
| 668 | |||
| 669 | state[aruid_index].npad_revision = revision; | ||
| 670 | } | ||
| 671 | |||
| 672 | Result NPadResource::SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled) { | ||
| 673 | const u64 aruid_index = GetIndexFromAruid(aruid); | ||
| 674 | if (aruid_index >= AruidIndexMax) { | ||
| 675 | return ResultNpadNotConnected; | ||
| 676 | } | ||
| 677 | |||
| 678 | state[aruid_index].data.SetNpadAnalogStickUseCenterClamp(is_enabled); | ||
| 679 | if (active_data_aruid == aruid) { | ||
| 680 | active_data.SetNpadAnalogStickUseCenterClamp(is_enabled); | ||
| 681 | } | ||
| 682 | return ResultSuccess; | ||
| 683 | } | ||
| 684 | |||
| 685 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/npad/npad_resource.h b/src/hid_core/resources/npad/npad_resource.h new file mode 100644 index 000000000..aed89eec6 --- /dev/null +++ b/src/hid_core/resources/npad/npad_resource.h | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <mutex> | ||
| 8 | #include <span> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | #include "core/hle/service/kernel_helpers.h" | ||
| 13 | #include "hid_core/hid_types.h" | ||
| 14 | #include "hid_core/resources/applet_resource.h" | ||
| 15 | #include "hid_core/resources/npad/npad_data.h" | ||
| 16 | #include "hid_core/resources/npad/npad_types.h" | ||
| 17 | |||
| 18 | namespace Core { | ||
| 19 | class System; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace Kernel { | ||
| 23 | class KReadableEvent; | ||
| 24 | } | ||
| 25 | |||
| 26 | namespace Service::HID { | ||
| 27 | struct DataStatusFlag; | ||
| 28 | |||
| 29 | struct NpadControllerState { | ||
| 30 | bool is_styleset_update_event_initialized{}; | ||
| 31 | INSERT_PADDING_BYTES(0x7); | ||
| 32 | Kernel::KEvent* style_set_update_event{nullptr}; | ||
| 33 | INSERT_PADDING_BYTES(0x27); | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct NpadState { | ||
| 37 | DataStatusFlag flag{}; | ||
| 38 | u64 aruid{}; | ||
| 39 | NPadData data{}; | ||
| 40 | std::array<std::array<Core::HID::NpadButton, StyleIndexCount>, MaxSupportedNpadIdTypes> | ||
| 41 | button_config; | ||
| 42 | std::array<NpadControllerState, MaxSupportedNpadIdTypes> controller_state; | ||
| 43 | NpadRevision npad_revision; | ||
| 44 | }; | ||
| 45 | |||
| 46 | /// Handles Npad request from HID interfaces | ||
| 47 | class NPadResource final { | ||
| 48 | public: | ||
| 49 | explicit NPadResource(KernelHelpers::ServiceContext& context); | ||
| 50 | ~NPadResource(); | ||
| 51 | |||
| 52 | NPadData* GetActiveData(); | ||
| 53 | u64 GetActiveDataAruid(); | ||
| 54 | |||
| 55 | Result RegisterAppletResourceUserId(u64 aruid); | ||
| 56 | void UnregisterAppletResourceUserId(u64 aruid); | ||
| 57 | |||
| 58 | void DestroyStyleSetUpdateEvents(u64 aruid); | ||
| 59 | |||
| 60 | Result Activate(u64 aruid); | ||
| 61 | Result Activate(); | ||
| 62 | Result Deactivate(); | ||
| 63 | |||
| 64 | void SetAppletResourceUserId(u64 aruid); | ||
| 65 | std::size_t GetIndexFromAruid(u64 aruid) const; | ||
| 66 | |||
| 67 | Result ApplyNpadSystemCommonPolicy(u64 aruid, bool is_full_policy); | ||
| 68 | Result ClearNpadSystemCommonPolicy(u64 aruid); | ||
| 69 | |||
| 70 | Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet style_set); | ||
| 71 | Result GetSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_Set, u64 aruid) const; | ||
| 72 | Result GetMaskedSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const; | ||
| 73 | Result GetAvailableStyleset(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const; | ||
| 74 | |||
| 75 | NpadRevision GetNpadRevision(u64 aruid) const; | ||
| 76 | void SetNpadRevision(u64 aruid, NpadRevision revision); | ||
| 77 | |||
| 78 | Result IsSupportedNpadStyleSet(bool& is_set, u64 aruid); | ||
| 79 | |||
| 80 | Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type); | ||
| 81 | Result GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const; | ||
| 82 | |||
| 83 | Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode activation_mode); | ||
| 84 | Result GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode, | ||
| 85 | u64 aruid) const; | ||
| 86 | |||
| 87 | Result SetSupportedNpadIdType(u64 aruid, | ||
| 88 | std::span<const Core::HID::NpadIdType> supported_npad_list); | ||
| 89 | bool IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const; | ||
| 90 | |||
| 91 | Result SetLrAssignmentMode(u64 aruid, bool is_enabled); | ||
| 92 | Result GetLrAssignmentMode(bool& is_enabled, u64 aruid) const; | ||
| 93 | |||
| 94 | Result SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled); | ||
| 95 | Result IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const; | ||
| 96 | |||
| 97 | Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event, | ||
| 98 | Core::HID::NpadIdType npad_id); | ||
| 99 | Result SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id); | ||
| 100 | |||
| 101 | Result GetHomeProtectionEnabled(bool& is_enabled, u64 aruid, | ||
| 102 | Core::HID::NpadIdType npad_id) const; | ||
| 103 | Result SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id, bool is_enabled); | ||
| 104 | |||
| 105 | Result SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled); | ||
| 106 | |||
| 107 | Result SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index, | ||
| 108 | Core::HID::NpadButton button_config); | ||
| 109 | Core::HID::NpadButton GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, | ||
| 110 | std::size_t index, Core::HID::NpadButton mask, | ||
| 111 | bool is_enabled); | ||
| 112 | void ResetButtonConfig(); | ||
| 113 | |||
| 114 | Result SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set, | ||
| 115 | Core::HID::NpadButton button_assignment); | ||
| 116 | Result ClearNpadCaptureButtonAssignment(u64 aruid); | ||
| 117 | std::size_t GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list, | ||
| 118 | u64 aruid) const; | ||
| 119 | |||
| 120 | Result SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled); | ||
| 121 | |||
| 122 | private: | ||
| 123 | NPadData active_data{}; | ||
| 124 | AruidRegisterList registration_list{}; | ||
| 125 | std::array<NpadState, AruidIndexMax> state{}; | ||
| 126 | u64 active_data_aruid{}; | ||
| 127 | NpadJoyHoldType default_hold_type{}; | ||
| 128 | s32 ref_counter{}; | ||
| 129 | |||
| 130 | KernelHelpers::ServiceContext& service_context; | ||
| 131 | }; | ||
| 132 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/npad/npad_types.h b/src/hid_core/resources/npad/npad_types.h new file mode 100644 index 000000000..a02f9cf16 --- /dev/null +++ b/src/hid_core/resources/npad/npad_types.h | |||
| @@ -0,0 +1,255 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/bit_field.h" | ||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "hid_core/hid_types.h" | ||
| 10 | |||
| 11 | namespace Service::HID { | ||
| 12 | static constexpr std::size_t MaxSupportedNpadIdTypes = 10; | ||
| 13 | static constexpr std::size_t StyleIndexCount = 7; | ||
| 14 | |||
| 15 | // This is nn::hid::NpadJoyHoldType | ||
| 16 | enum class NpadJoyHoldType : u64 { | ||
| 17 | Vertical = 0, | ||
| 18 | Horizontal = 1, | ||
| 19 | }; | ||
| 20 | |||
| 21 | // This is nn::hid::NpadJoyAssignmentMode | ||
| 22 | enum class NpadJoyAssignmentMode : u32 { | ||
| 23 | Dual = 0, | ||
| 24 | Single = 1, | ||
| 25 | }; | ||
| 26 | |||
| 27 | // This is nn::hid::NpadJoyDeviceType | ||
| 28 | enum class NpadJoyDeviceType : s64 { | ||
| 29 | Left = 0, | ||
| 30 | Right = 1, | ||
| 31 | }; | ||
| 32 | |||
| 33 | // This is nn::hid::NpadHandheldActivationMode | ||
| 34 | enum class NpadHandheldActivationMode : u64 { | ||
| 35 | Dual = 0, | ||
| 36 | Single = 1, | ||
| 37 | None = 2, | ||
| 38 | MaxActivationMode = 3, | ||
| 39 | }; | ||
| 40 | |||
| 41 | // This is nn::hid::system::AppletFooterUiAttributesSet | ||
| 42 | struct AppletFooterUiAttributes { | ||
| 43 | INSERT_PADDING_BYTES(0x4); | ||
| 44 | }; | ||
| 45 | |||
| 46 | // This is nn::hid::system::AppletFooterUiType | ||
| 47 | enum class AppletFooterUiType : u8 { | ||
| 48 | None = 0, | ||
| 49 | HandheldNone = 1, | ||
| 50 | HandheldJoyConLeftOnly = 2, | ||
| 51 | HandheldJoyConRightOnly = 3, | ||
| 52 | HandheldJoyConLeftJoyConRight = 4, | ||
| 53 | JoyDual = 5, | ||
| 54 | JoyDualLeftOnly = 6, | ||
| 55 | JoyDualRightOnly = 7, | ||
| 56 | JoyLeftHorizontal = 8, | ||
| 57 | JoyLeftVertical = 9, | ||
| 58 | JoyRightHorizontal = 10, | ||
| 59 | JoyRightVertical = 11, | ||
| 60 | SwitchProController = 12, | ||
| 61 | CompatibleProController = 13, | ||
| 62 | CompatibleJoyCon = 14, | ||
| 63 | LarkHvc1 = 15, | ||
| 64 | LarkHvc2 = 16, | ||
| 65 | LarkNesLeft = 17, | ||
| 66 | LarkNesRight = 18, | ||
| 67 | Lucia = 19, | ||
| 68 | Verification = 20, | ||
| 69 | Lagon = 21, | ||
| 70 | }; | ||
| 71 | |||
| 72 | using AppletFooterUiVariant = u8; | ||
| 73 | |||
| 74 | // This is "nn::hid::system::AppletDetailedUiType". | ||
| 75 | struct AppletDetailedUiType { | ||
| 76 | AppletFooterUiVariant ui_variant; | ||
| 77 | INSERT_PADDING_BYTES(0x2); | ||
| 78 | AppletFooterUiType footer; | ||
| 79 | }; | ||
| 80 | static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size"); | ||
| 81 | // This is nn::hid::NpadCommunicationMode | ||
| 82 | enum class NpadCommunicationMode : u64 { | ||
| 83 | Mode_5ms = 0, | ||
| 84 | Mode_10ms = 1, | ||
| 85 | Mode_15ms = 2, | ||
| 86 | Default = 3, | ||
| 87 | }; | ||
| 88 | |||
| 89 | enum class NpadRevision : u32 { | ||
| 90 | Revision0 = 0, | ||
| 91 | Revision1 = 1, | ||
| 92 | Revision2 = 2, | ||
| 93 | Revision3 = 3, | ||
| 94 | }; | ||
| 95 | |||
| 96 | // This is nn::hid::detail::ColorAttribute | ||
| 97 | enum class ColorAttribute : u32 { | ||
| 98 | Ok = 0, | ||
| 99 | ReadError = 1, | ||
| 100 | NoController = 2, | ||
| 101 | }; | ||
| 102 | static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size"); | ||
| 103 | |||
| 104 | // This is nn::hid::detail::NpadFullKeyColorState | ||
| 105 | struct NpadFullKeyColorState { | ||
| 106 | ColorAttribute attribute{ColorAttribute::NoController}; | ||
| 107 | Core::HID::NpadControllerColor fullkey{}; | ||
| 108 | }; | ||
| 109 | static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); | ||
| 110 | |||
| 111 | // This is nn::hid::detail::NpadJoyColorState | ||
| 112 | struct NpadJoyColorState { | ||
| 113 | ColorAttribute attribute{ColorAttribute::NoController}; | ||
| 114 | Core::HID::NpadControllerColor left{}; | ||
| 115 | Core::HID::NpadControllerColor right{}; | ||
| 116 | }; | ||
| 117 | static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); | ||
| 118 | |||
| 119 | // This is nn::hid::NpadAttribute | ||
| 120 | struct NpadAttribute { | ||
| 121 | union { | ||
| 122 | u32 raw{}; | ||
| 123 | BitField<0, 1, u32> is_connected; | ||
| 124 | BitField<1, 1, u32> is_wired; | ||
| 125 | BitField<2, 1, u32> is_left_connected; | ||
| 126 | BitField<3, 1, u32> is_left_wired; | ||
| 127 | BitField<4, 1, u32> is_right_connected; | ||
| 128 | BitField<5, 1, u32> is_right_wired; | ||
| 129 | }; | ||
| 130 | }; | ||
| 131 | static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size"); | ||
| 132 | |||
| 133 | // This is nn::hid::NpadFullKeyState | ||
| 134 | // This is nn::hid::NpadHandheldState | ||
| 135 | // This is nn::hid::NpadJoyDualState | ||
| 136 | // This is nn::hid::NpadJoyLeftState | ||
| 137 | // This is nn::hid::NpadJoyRightState | ||
| 138 | // This is nn::hid::NpadPalmaState | ||
| 139 | // This is nn::hid::NpadSystemExtState | ||
| 140 | struct NPadGenericState { | ||
| 141 | s64_le sampling_number{}; | ||
| 142 | Core::HID::NpadButtonState npad_buttons{}; | ||
| 143 | Core::HID::AnalogStickState l_stick{}; | ||
| 144 | Core::HID::AnalogStickState r_stick{}; | ||
| 145 | NpadAttribute connection_status{}; | ||
| 146 | INSERT_PADDING_BYTES(4); // Reserved | ||
| 147 | }; | ||
| 148 | static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); | ||
| 149 | |||
| 150 | // This is nn::hid::server::NpadGcTriggerState | ||
| 151 | struct NpadGcTriggerState { | ||
| 152 | s64 sampling_number{}; | ||
| 153 | s32 l_analog{}; | ||
| 154 | s32 r_analog{}; | ||
| 155 | }; | ||
| 156 | static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); | ||
| 157 | |||
| 158 | // This is nn::hid::NpadSystemProperties | ||
| 159 | struct NPadSystemProperties { | ||
| 160 | union { | ||
| 161 | s64 raw{}; | ||
| 162 | BitField<0, 1, s64> is_charging_joy_dual; | ||
| 163 | BitField<1, 1, s64> is_charging_joy_left; | ||
| 164 | BitField<2, 1, s64> is_charging_joy_right; | ||
| 165 | BitField<3, 1, s64> is_powered_joy_dual; | ||
| 166 | BitField<4, 1, s64> is_powered_joy_left; | ||
| 167 | BitField<5, 1, s64> is_powered_joy_right; | ||
| 168 | BitField<9, 1, s64> is_system_unsupported_button; | ||
| 169 | BitField<10, 1, s64> is_system_ext_unsupported_button; | ||
| 170 | BitField<11, 1, s64> is_vertical; | ||
| 171 | BitField<12, 1, s64> is_horizontal; | ||
| 172 | BitField<13, 1, s64> use_plus; | ||
| 173 | BitField<14, 1, s64> use_minus; | ||
| 174 | BitField<15, 1, s64> use_directional_buttons; | ||
| 175 | }; | ||
| 176 | }; | ||
| 177 | static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size"); | ||
| 178 | |||
| 179 | // This is nn::hid::NpadSystemButtonProperties | ||
| 180 | struct NpadSystemButtonProperties { | ||
| 181 | union { | ||
| 182 | s32 raw{}; | ||
| 183 | BitField<0, 1, s32> is_home_button_protection_enabled; | ||
| 184 | }; | ||
| 185 | }; | ||
| 186 | static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size"); | ||
| 187 | |||
| 188 | // This is nn::hid::system::DeviceType | ||
| 189 | struct DeviceType { | ||
| 190 | union { | ||
| 191 | u32 raw{}; | ||
| 192 | BitField<0, 1, s32> fullkey; | ||
| 193 | BitField<1, 1, s32> debug_pad; | ||
| 194 | BitField<2, 1, s32> handheld_left; | ||
| 195 | BitField<3, 1, s32> handheld_right; | ||
| 196 | BitField<4, 1, s32> joycon_left; | ||
| 197 | BitField<5, 1, s32> joycon_right; | ||
| 198 | BitField<6, 1, s32> palma; | ||
| 199 | BitField<7, 1, s32> lark_hvc_left; | ||
| 200 | BitField<8, 1, s32> lark_hvc_right; | ||
| 201 | BitField<9, 1, s32> lark_nes_left; | ||
| 202 | BitField<10, 1, s32> lark_nes_right; | ||
| 203 | BitField<11, 1, s32> handheld_lark_hvc_left; | ||
| 204 | BitField<12, 1, s32> handheld_lark_hvc_right; | ||
| 205 | BitField<13, 1, s32> handheld_lark_nes_left; | ||
| 206 | BitField<14, 1, s32> handheld_lark_nes_right; | ||
| 207 | BitField<15, 1, s32> lucia; | ||
| 208 | BitField<16, 1, s32> lagon; | ||
| 209 | BitField<17, 1, s32> lager; | ||
| 210 | BitField<31, 1, s32> system; | ||
| 211 | }; | ||
| 212 | }; | ||
| 213 | |||
| 214 | // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl | ||
| 215 | struct NfcXcdDeviceHandleStateImpl { | ||
| 216 | u64 handle{}; | ||
| 217 | bool is_available{}; | ||
| 218 | bool is_activated{}; | ||
| 219 | INSERT_PADDING_BYTES(0x6); // Reserved | ||
| 220 | u64 sampling_number{}; | ||
| 221 | }; | ||
| 222 | static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, | ||
| 223 | "NfcXcdDeviceHandleStateImpl is an invalid size"); | ||
| 224 | |||
| 225 | // This is nn::hid::NpadLarkType | ||
| 226 | enum class NpadLarkType : u32 { | ||
| 227 | Invalid, | ||
| 228 | H1, | ||
| 229 | H2, | ||
| 230 | NL, | ||
| 231 | NR, | ||
| 232 | }; | ||
| 233 | |||
| 234 | // This is nn::hid::NpadLuciaType | ||
| 235 | enum class NpadLuciaType : u32 { | ||
| 236 | Invalid, | ||
| 237 | J, | ||
| 238 | E, | ||
| 239 | U, | ||
| 240 | }; | ||
| 241 | |||
| 242 | // This is nn::hid::NpadLagonType | ||
| 243 | enum class NpadLagonType : u32 { | ||
| 244 | Invalid, | ||
| 245 | }; | ||
| 246 | |||
| 247 | // This is nn::hid::NpadLagerType | ||
| 248 | enum class NpadLagerType : u32 { | ||
| 249 | Invalid, | ||
| 250 | J, | ||
| 251 | E, | ||
| 252 | U, | ||
| 253 | }; | ||
| 254 | |||
| 255 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/palma/palma.cpp b/src/hid_core/resources/palma/palma.cpp new file mode 100644 index 000000000..ea4a291fd --- /dev/null +++ b/src/hid_core/resources/palma/palma.cpp | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "core/hle/kernel/k_event.h" | ||
| 6 | #include "core/hle/kernel/k_readable_event.h" | ||
| 7 | #include "core/hle/service/kernel_helpers.h" | ||
| 8 | #include "hid_core/frontend/emulated_controller.h" | ||
| 9 | #include "hid_core/hid_core.h" | ||
| 10 | #include "hid_core/resources/palma/palma.h" | ||
| 11 | |||
| 12 | namespace Service::HID { | ||
| 13 | |||
| 14 | Palma::Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_) | ||
| 15 | : ControllerBase{hid_core_}, service_context{service_context_} { | ||
| 16 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); | ||
| 17 | operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); | ||
| 18 | } | ||
| 19 | |||
| 20 | Palma::~Palma() { | ||
| 21 | service_context.CloseEvent(operation_complete_event); | ||
| 22 | }; | ||
| 23 | |||
| 24 | void Palma::OnInit() {} | ||
| 25 | |||
| 26 | void Palma::OnRelease() {} | ||
| 27 | |||
| 28 | void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 29 | if (!IsControllerActivated()) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, | ||
| 35 | PalmaConnectionHandle& handle) { | ||
| 36 | active_handle.npad_id = npad_id; | ||
| 37 | handle = active_handle; | ||
| 38 | return ResultSuccess; | ||
| 39 | } | ||
| 40 | |||
| 41 | Result Palma::InitializePalma(const PalmaConnectionHandle& handle) { | ||
| 42 | if (handle.npad_id != active_handle.npad_id) { | ||
| 43 | return InvalidPalmaHandle; | ||
| 44 | } | ||
| 45 | Activate(); | ||
| 46 | return ResultSuccess; | ||
| 47 | } | ||
| 48 | |||
| 49 | Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent( | ||
| 50 | const PalmaConnectionHandle& handle) const { | ||
| 51 | if (handle.npad_id != active_handle.npad_id) { | ||
| 52 | LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); | ||
| 53 | } | ||
| 54 | return operation_complete_event->GetReadableEvent(); | ||
| 55 | } | ||
| 56 | |||
| 57 | Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, | ||
| 58 | PalmaOperationType& operation_type, | ||
| 59 | PalmaOperationData& data) const { | ||
| 60 | if (handle.npad_id != active_handle.npad_id) { | ||
| 61 | return InvalidPalmaHandle; | ||
| 62 | } | ||
| 63 | operation_type = operation.operation; | ||
| 64 | data = operation.data; | ||
| 65 | return ResultSuccess; | ||
| 66 | } | ||
| 67 | |||
| 68 | Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) { | ||
| 69 | if (handle.npad_id != active_handle.npad_id) { | ||
| 70 | return InvalidPalmaHandle; | ||
| 71 | } | ||
| 72 | operation.operation = PalmaOperationType::PlayActivity; | ||
| 73 | operation.result = PalmaResultSuccess; | ||
| 74 | operation.data = {}; | ||
| 75 | operation_complete_event->Signal(); | ||
| 76 | return ResultSuccess; | ||
| 77 | } | ||
| 78 | |||
| 79 | Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) { | ||
| 80 | if (handle.npad_id != active_handle.npad_id) { | ||
| 81 | return InvalidPalmaHandle; | ||
| 82 | } | ||
| 83 | fr_mode = fr_mode_; | ||
| 84 | return ResultSuccess; | ||
| 85 | } | ||
| 86 | |||
| 87 | Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { | ||
| 88 | if (handle.npad_id != active_handle.npad_id) { | ||
| 89 | return InvalidPalmaHandle; | ||
| 90 | } | ||
| 91 | operation.operation = PalmaOperationType::ReadStep; | ||
| 92 | operation.result = PalmaResultSuccess; | ||
| 93 | operation.data = {}; | ||
| 94 | operation_complete_event->Signal(); | ||
| 95 | return ResultSuccess; | ||
| 96 | } | ||
| 97 | |||
| 98 | Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { | ||
| 99 | if (handle.npad_id != active_handle.npad_id) { | ||
| 100 | return InvalidPalmaHandle; | ||
| 101 | } | ||
| 102 | return ResultSuccess; | ||
| 103 | } | ||
| 104 | |||
| 105 | Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { | ||
| 106 | if (handle.npad_id != active_handle.npad_id) { | ||
| 107 | return InvalidPalmaHandle; | ||
| 108 | } | ||
| 109 | return ResultSuccess; | ||
| 110 | } | ||
| 111 | |||
| 112 | void Palma::ReadPalmaApplicationSection() {} | ||
| 113 | |||
| 114 | void Palma::WritePalmaApplicationSection() {} | ||
| 115 | |||
| 116 | Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { | ||
| 117 | if (handle.npad_id != active_handle.npad_id) { | ||
| 118 | return InvalidPalmaHandle; | ||
| 119 | } | ||
| 120 | operation.operation = PalmaOperationType::ReadUniqueCode; | ||
| 121 | operation.result = PalmaResultSuccess; | ||
| 122 | operation.data = {}; | ||
| 123 | operation_complete_event->Signal(); | ||
| 124 | return ResultSuccess; | ||
| 125 | } | ||
| 126 | |||
| 127 | Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { | ||
| 128 | if (handle.npad_id != active_handle.npad_id) { | ||
| 129 | return InvalidPalmaHandle; | ||
| 130 | } | ||
| 131 | operation.operation = PalmaOperationType::SetUniqueCodeInvalid; | ||
| 132 | operation.result = PalmaResultSuccess; | ||
| 133 | operation.data = {}; | ||
| 134 | operation_complete_event->Signal(); | ||
| 135 | return ResultSuccess; | ||
| 136 | } | ||
| 137 | |||
| 138 | void Palma::WritePalmaActivityEntry() {} | ||
| 139 | |||
| 140 | Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) { | ||
| 141 | if (handle.npad_id != active_handle.npad_id) { | ||
| 142 | return InvalidPalmaHandle; | ||
| 143 | } | ||
| 144 | operation.operation = PalmaOperationType::WriteRgbLedPatternEntry; | ||
| 145 | operation.result = PalmaResultSuccess; | ||
| 146 | operation.data = {}; | ||
| 147 | operation_complete_event->Signal(); | ||
| 148 | return ResultSuccess; | ||
| 149 | } | ||
| 150 | |||
| 151 | Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, | ||
| 152 | Common::ProcessAddress t_mem, u64 size) { | ||
| 153 | if (handle.npad_id != active_handle.npad_id) { | ||
| 154 | return InvalidPalmaHandle; | ||
| 155 | } | ||
| 156 | operation.operation = PalmaOperationType::WriteWaveEntry; | ||
| 157 | operation.result = PalmaResultSuccess; | ||
| 158 | operation.data = {}; | ||
| 159 | operation_complete_event->Signal(); | ||
| 160 | return ResultSuccess; | ||
| 161 | } | ||
| 162 | |||
| 163 | Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, | ||
| 164 | s32 database_id_version_) { | ||
| 165 | if (handle.npad_id != active_handle.npad_id) { | ||
| 166 | return InvalidPalmaHandle; | ||
| 167 | } | ||
| 168 | database_id_version = database_id_version_; | ||
| 169 | operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; | ||
| 170 | operation.result = PalmaResultSuccess; | ||
| 171 | operation.data[0] = {}; | ||
| 172 | operation_complete_event->Signal(); | ||
| 173 | return ResultSuccess; | ||
| 174 | } | ||
| 175 | |||
| 176 | Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) { | ||
| 177 | if (handle.npad_id != active_handle.npad_id) { | ||
| 178 | return InvalidPalmaHandle; | ||
| 179 | } | ||
| 180 | operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; | ||
| 181 | operation.result = PalmaResultSuccess; | ||
| 182 | operation.data = {}; | ||
| 183 | operation.data[0] = static_cast<u8>(database_id_version); | ||
| 184 | operation_complete_event->Signal(); | ||
| 185 | return ResultSuccess; | ||
| 186 | } | ||
| 187 | |||
| 188 | void Palma::SuspendPalmaFeature() {} | ||
| 189 | |||
| 190 | Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { | ||
| 191 | if (handle.npad_id != active_handle.npad_id) { | ||
| 192 | return InvalidPalmaHandle; | ||
| 193 | } | ||
| 194 | return operation.result; | ||
| 195 | } | ||
| 196 | void Palma::ReadPalmaPlayLog() {} | ||
| 197 | |||
| 198 | void Palma::ResetPalmaPlayLog() {} | ||
| 199 | |||
| 200 | void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { | ||
| 201 | // If true controllers are able to be paired | ||
| 202 | is_connectable = is_all_connectable; | ||
| 203 | } | ||
| 204 | |||
| 205 | void Palma::SetIsPalmaPairedConnectable() {} | ||
| 206 | |||
| 207 | Result Palma::PairPalma(const PalmaConnectionHandle& handle) { | ||
| 208 | if (handle.npad_id != active_handle.npad_id) { | ||
| 209 | return InvalidPalmaHandle; | ||
| 210 | } | ||
| 211 | // TODO: Do something | ||
| 212 | return ResultSuccess; | ||
| 213 | } | ||
| 214 | |||
| 215 | void Palma::SetPalmaBoostMode(bool boost_mode) {} | ||
| 216 | |||
| 217 | void Palma::CancelWritePalmaWaveEntry() {} | ||
| 218 | |||
| 219 | void Palma::EnablePalmaBoostMode() {} | ||
| 220 | |||
| 221 | void Palma::GetPalmaBluetoothAddress() {} | ||
| 222 | |||
| 223 | void Palma::SetDisallowedPalmaConnection() {} | ||
| 224 | |||
| 225 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/palma/palma.h b/src/hid_core/resources/palma/palma.h new file mode 100644 index 000000000..60259c3d8 --- /dev/null +++ b/src/hid_core/resources/palma/palma.h | |||
| @@ -0,0 +1,163 @@ | |||
| 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 "common/common_funcs.h" | ||
| 8 | #include "common/typed_address.h" | ||
| 9 | #include "hid_core/hid_result.h" | ||
| 10 | #include "hid_core/hid_types.h" | ||
| 11 | #include "hid_core/resources/controller_base.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | class KEvent; | ||
| 15 | class KReadableEvent; | ||
| 16 | } // namespace Kernel | ||
| 17 | |||
| 18 | namespace Service::KernelHelpers { | ||
| 19 | class ServiceContext; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace Core::HID { | ||
| 23 | class EmulatedController; | ||
| 24 | } // namespace Core::HID | ||
| 25 | |||
| 26 | namespace Service::HID { | ||
| 27 | class Palma final : public ControllerBase { | ||
| 28 | public: | ||
| 29 | using PalmaOperationData = std::array<u8, 0x140>; | ||
| 30 | |||
| 31 | // This is nn::hid::PalmaOperationType | ||
| 32 | enum class PalmaOperationType { | ||
| 33 | PlayActivity, | ||
| 34 | SetFrModeType, | ||
| 35 | ReadStep, | ||
| 36 | EnableStep, | ||
| 37 | ResetStep, | ||
| 38 | ReadApplicationSection, | ||
| 39 | WriteApplicationSection, | ||
| 40 | ReadUniqueCode, | ||
| 41 | SetUniqueCodeInvalid, | ||
| 42 | WriteActivityEntry, | ||
| 43 | WriteRgbLedPatternEntry, | ||
| 44 | WriteWaveEntry, | ||
| 45 | ReadDataBaseIdentificationVersion, | ||
| 46 | WriteDataBaseIdentificationVersion, | ||
| 47 | SuspendFeature, | ||
| 48 | ReadPlayLog, | ||
| 49 | ResetPlayLog, | ||
| 50 | }; | ||
| 51 | |||
| 52 | // This is nn::hid::PalmaWaveSet | ||
| 53 | enum class PalmaWaveSet : u64 { | ||
| 54 | Small, | ||
| 55 | Medium, | ||
| 56 | Large, | ||
| 57 | }; | ||
| 58 | |||
| 59 | // This is nn::hid::PalmaFrModeType | ||
| 60 | enum class PalmaFrModeType : u64 { | ||
| 61 | Off, | ||
| 62 | B01, | ||
| 63 | B02, | ||
| 64 | B03, | ||
| 65 | Downloaded, | ||
| 66 | }; | ||
| 67 | |||
| 68 | // This is nn::hid::PalmaFeature | ||
| 69 | enum class PalmaFeature : u64 { | ||
| 70 | FrMode, | ||
| 71 | RumbleFeedback, | ||
| 72 | Step, | ||
| 73 | MuteSwitch, | ||
| 74 | }; | ||
| 75 | |||
| 76 | // This is nn::hid::PalmaOperationInfo | ||
| 77 | struct PalmaOperationInfo { | ||
| 78 | PalmaOperationType operation{}; | ||
| 79 | Result result{PalmaResultSuccess}; | ||
| 80 | PalmaOperationData data{}; | ||
| 81 | }; | ||
| 82 | static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size"); | ||
| 83 | |||
| 84 | // This is nn::hid::PalmaActivityEntry | ||
| 85 | struct PalmaActivityEntry { | ||
| 86 | u32 rgb_led_pattern_index; | ||
| 87 | INSERT_PADDING_BYTES(2); | ||
| 88 | PalmaWaveSet wave_set; | ||
| 89 | u32 wave_index; | ||
| 90 | INSERT_PADDING_BYTES(12); | ||
| 91 | }; | ||
| 92 | static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size"); | ||
| 93 | |||
| 94 | struct PalmaConnectionHandle { | ||
| 95 | Core::HID::NpadIdType npad_id; | ||
| 96 | INSERT_PADDING_BYTES(4); // Unknown | ||
| 97 | }; | ||
| 98 | static_assert(sizeof(PalmaConnectionHandle) == 0x8, | ||
| 99 | "PalmaConnectionHandle has incorrect size."); | ||
| 100 | |||
| 101 | explicit Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_); | ||
| 102 | ~Palma() override; | ||
| 103 | |||
| 104 | // Called when the controller is initialized | ||
| 105 | void OnInit() override; | ||
| 106 | |||
| 107 | // When the controller is released | ||
| 108 | void OnRelease() override; | ||
| 109 | |||
| 110 | // When the controller is requesting an update for the shared memory | ||
| 111 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 112 | |||
| 113 | Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle); | ||
| 114 | Result InitializePalma(const PalmaConnectionHandle& handle); | ||
| 115 | Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent( | ||
| 116 | const PalmaConnectionHandle& handle) const; | ||
| 117 | Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle, | ||
| 118 | PalmaOperationType& operation_type, | ||
| 119 | PalmaOperationData& data) const; | ||
| 120 | Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity); | ||
| 121 | Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_); | ||
| 122 | Result ReadPalmaStep(const PalmaConnectionHandle& handle); | ||
| 123 | Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled); | ||
| 124 | Result ResetPalmaStep(const PalmaConnectionHandle& handle); | ||
| 125 | Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle); | ||
| 126 | Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle); | ||
| 127 | Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown); | ||
| 128 | Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, | ||
| 129 | Common::ProcessAddress t_mem, u64 size); | ||
| 130 | Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, | ||
| 131 | s32 database_id_version_); | ||
| 132 | Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle); | ||
| 133 | Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const; | ||
| 134 | void SetIsPalmaAllConnectable(bool is_all_connectable); | ||
| 135 | Result PairPalma(const PalmaConnectionHandle& handle); | ||
| 136 | void SetPalmaBoostMode(bool boost_mode); | ||
| 137 | |||
| 138 | private: | ||
| 139 | void ReadPalmaApplicationSection(); | ||
| 140 | void WritePalmaApplicationSection(); | ||
| 141 | void WritePalmaActivityEntry(); | ||
| 142 | void SuspendPalmaFeature(); | ||
| 143 | void ReadPalmaPlayLog(); | ||
| 144 | void ResetPalmaPlayLog(); | ||
| 145 | void SetIsPalmaPairedConnectable(); | ||
| 146 | void CancelWritePalmaWaveEntry(); | ||
| 147 | void EnablePalmaBoostMode(); | ||
| 148 | void GetPalmaBluetoothAddress(); | ||
| 149 | void SetDisallowedPalmaConnection(); | ||
| 150 | |||
| 151 | bool is_connectable{}; | ||
| 152 | s32 database_id_version{}; | ||
| 153 | PalmaOperationInfo operation{}; | ||
| 154 | PalmaFrModeType fr_mode{}; | ||
| 155 | PalmaConnectionHandle active_handle{}; | ||
| 156 | |||
| 157 | Core::HID::EmulatedController* controller; | ||
| 158 | |||
| 159 | Kernel::KEvent* operation_complete_event; | ||
| 160 | KernelHelpers::ServiceContext& service_context; | ||
| 161 | }; | ||
| 162 | |||
| 163 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/ring_lifo.h b/src/hid_core/resources/ring_lifo.h new file mode 100644 index 000000000..0816784e0 --- /dev/null +++ b/src/hid_core/resources/ring_lifo.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace Service::HID { | ||
| 11 | |||
| 12 | template <typename State> | ||
| 13 | struct AtomicStorage { | ||
| 14 | s64 sampling_number; | ||
| 15 | State state; | ||
| 16 | }; | ||
| 17 | |||
| 18 | template <typename State, std::size_t max_buffer_size> | ||
| 19 | struct Lifo { | ||
| 20 | s64 timestamp{}; | ||
| 21 | s64 total_buffer_count = static_cast<s64>(max_buffer_size); | ||
| 22 | s64 buffer_tail{}; | ||
| 23 | s64 buffer_count{}; | ||
| 24 | std::array<AtomicStorage<State>, max_buffer_size> entries{}; | ||
| 25 | |||
| 26 | const AtomicStorage<State>& ReadCurrentEntry() const { | ||
| 27 | return entries[buffer_tail]; | ||
| 28 | } | ||
| 29 | |||
| 30 | const AtomicStorage<State>& ReadPreviousEntry() const { | ||
| 31 | return entries[GetPreviousEntryIndex()]; | ||
| 32 | } | ||
| 33 | |||
| 34 | std::size_t GetPreviousEntryIndex() const { | ||
| 35 | return static_cast<size_t>((buffer_tail + max_buffer_size - 1) % max_buffer_size); | ||
| 36 | } | ||
| 37 | |||
| 38 | std::size_t GetNextEntryIndex() const { | ||
| 39 | return static_cast<size_t>((buffer_tail + 1) % max_buffer_size); | ||
| 40 | } | ||
| 41 | |||
| 42 | void WriteNextEntry(const State& new_state) { | ||
| 43 | if (buffer_count < static_cast<s64>(max_buffer_size) - 1) { | ||
| 44 | buffer_count++; | ||
| 45 | } | ||
| 46 | buffer_tail = GetNextEntryIndex(); | ||
| 47 | const auto& previous_entry = ReadPreviousEntry(); | ||
| 48 | entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1; | ||
| 49 | entries[buffer_tail].state = new_state; | ||
| 50 | } | ||
| 51 | }; | ||
| 52 | |||
| 53 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/shared_memory_format.h b/src/hid_core/resources/shared_memory_format.h new file mode 100644 index 000000000..2ae0004ba --- /dev/null +++ b/src/hid_core/resources/shared_memory_format.h | |||
| @@ -0,0 +1,240 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/vector_math.h" | ||
| 9 | #include "hid_core/hid_types.h" | ||
| 10 | #include "hid_core/resources/debug_pad/debug_pad_types.h" | ||
| 11 | #include "hid_core/resources/keyboard/keyboard_types.h" | ||
| 12 | #include "hid_core/resources/mouse/mouse_types.h" | ||
| 13 | #include "hid_core/resources/npad/npad_types.h" | ||
| 14 | #include "hid_core/resources/ring_lifo.h" | ||
| 15 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 16 | |||
| 17 | namespace Service::HID { | ||
| 18 | static const std::size_t HidEntryCount = 17; | ||
| 19 | |||
| 20 | struct CommonHeader { | ||
| 21 | s64 timestamp{}; | ||
| 22 | s64 total_entry_count{}; | ||
| 23 | s64 last_entry_index{}; | ||
| 24 | s64 entry_count{}; | ||
| 25 | }; | ||
| 26 | static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); | ||
| 27 | |||
| 28 | // This is nn::hid::detail::DebugPadSharedMemoryFormat | ||
| 29 | struct DebugPadSharedMemoryFormat { | ||
| 30 | // This is nn::hid::detail::DebugPadLifo | ||
| 31 | Lifo<DebugPadState, HidEntryCount> debug_pad_lifo{}; | ||
| 32 | static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); | ||
| 33 | INSERT_PADDING_WORDS(0x4E); | ||
| 34 | }; | ||
| 35 | static_assert(sizeof(DebugPadSharedMemoryFormat) == 0x400, | ||
| 36 | "DebugPadSharedMemoryFormat is an invalid size"); | ||
| 37 | |||
| 38 | // This is nn::hid::detail::TouchScreenSharedMemoryFormat | ||
| 39 | struct TouchScreenSharedMemoryFormat { | ||
| 40 | // This is nn::hid::detail::TouchScreenLifo | ||
| 41 | Lifo<TouchScreenState, HidEntryCount> touch_screen_lifo{}; | ||
| 42 | static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); | ||
| 43 | INSERT_PADDING_WORDS(0xF2); | ||
| 44 | }; | ||
| 45 | static_assert(sizeof(TouchScreenSharedMemoryFormat) == 0x3000, | ||
| 46 | "TouchScreenSharedMemoryFormat is an invalid size"); | ||
| 47 | |||
| 48 | // This is nn::hid::detail::MouseSharedMemoryFormat | ||
| 49 | struct MouseSharedMemoryFormat { | ||
| 50 | // This is nn::hid::detail::MouseLifo | ||
| 51 | Lifo<Core::HID::MouseState, HidEntryCount> mouse_lifo{}; | ||
| 52 | static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); | ||
| 53 | INSERT_PADDING_WORDS(0x2C); | ||
| 54 | }; | ||
| 55 | static_assert(sizeof(MouseSharedMemoryFormat) == 0x400, | ||
| 56 | "MouseSharedMemoryFormat is an invalid size"); | ||
| 57 | |||
| 58 | // This is nn::hid::detail::KeyboardSharedMemoryFormat | ||
| 59 | struct KeyboardSharedMemoryFormat { | ||
| 60 | // This is nn::hid::detail::KeyboardLifo | ||
| 61 | Lifo<KeyboardState, HidEntryCount> keyboard_lifo{}; | ||
| 62 | static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); | ||
| 63 | INSERT_PADDING_WORDS(0xA); | ||
| 64 | }; | ||
| 65 | static_assert(sizeof(KeyboardSharedMemoryFormat) == 0x400, | ||
| 66 | "KeyboardSharedMemoryFormat is an invalid size"); | ||
| 67 | |||
| 68 | // This is nn::hid::detail::DigitizerSharedMemoryFormat | ||
| 69 | struct DigitizerSharedMemoryFormat { | ||
| 70 | CommonHeader header; | ||
| 71 | INSERT_PADDING_BYTES(0xFE0); | ||
| 72 | }; | ||
| 73 | static_assert(sizeof(DigitizerSharedMemoryFormat) == 0x1000, | ||
| 74 | "DigitizerSharedMemoryFormat is an invalid size"); | ||
| 75 | |||
| 76 | // This is nn::hid::detail::HomeButtonSharedMemoryFormat | ||
| 77 | struct HomeButtonSharedMemoryFormat { | ||
| 78 | CommonHeader header; | ||
| 79 | INSERT_PADDING_BYTES(0x1E0); | ||
| 80 | }; | ||
| 81 | static_assert(sizeof(HomeButtonSharedMemoryFormat) == 0x200, | ||
| 82 | "HomeButtonSharedMemoryFormat is an invalid size"); | ||
| 83 | |||
| 84 | // This is nn::hid::detail::SleepButtonSharedMemoryFormat | ||
| 85 | struct SleepButtonSharedMemoryFormat { | ||
| 86 | CommonHeader header; | ||
| 87 | INSERT_PADDING_BYTES(0x1E0); | ||
| 88 | }; | ||
| 89 | static_assert(sizeof(SleepButtonSharedMemoryFormat) == 0x200, | ||
| 90 | "SleepButtonSharedMemoryFormat is an invalid size"); | ||
| 91 | |||
| 92 | // This is nn::hid::detail::CaptureButtonSharedMemoryFormat | ||
| 93 | struct CaptureButtonSharedMemoryFormat { | ||
| 94 | CommonHeader header; | ||
| 95 | INSERT_PADDING_BYTES(0x1E0); | ||
| 96 | }; | ||
| 97 | static_assert(sizeof(CaptureButtonSharedMemoryFormat) == 0x200, | ||
| 98 | "CaptureButtonSharedMemoryFormat is an invalid size"); | ||
| 99 | |||
| 100 | // This is nn::hid::detail::InputDetectorSharedMemoryFormat | ||
| 101 | struct InputDetectorSharedMemoryFormat { | ||
| 102 | CommonHeader header; | ||
| 103 | INSERT_PADDING_BYTES(0x7E0); | ||
| 104 | }; | ||
| 105 | static_assert(sizeof(InputDetectorSharedMemoryFormat) == 0x800, | ||
| 106 | "InputDetectorSharedMemoryFormat is an invalid size"); | ||
| 107 | |||
| 108 | // This is nn::hid::detail::UniquePadSharedMemoryFormat | ||
| 109 | struct UniquePadSharedMemoryFormat { | ||
| 110 | CommonHeader header; | ||
| 111 | INSERT_PADDING_BYTES(0x3FE0); | ||
| 112 | }; | ||
| 113 | static_assert(sizeof(UniquePadSharedMemoryFormat) == 0x4000, | ||
| 114 | "UniquePadSharedMemoryFormat is an invalid size"); | ||
| 115 | |||
| 116 | // This is nn::hid::detail::NpadSixAxisSensorLifo | ||
| 117 | struct NpadSixAxisSensorLifo { | ||
| 118 | Lifo<Core::HID::SixAxisSensorState, HidEntryCount> lifo; | ||
| 119 | }; | ||
| 120 | |||
| 121 | // This is nn::hid::detail::NpadInternalState | ||
| 122 | struct NpadInternalState { | ||
| 123 | Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; | ||
| 124 | NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; | ||
| 125 | NpadFullKeyColorState fullkey_color{}; | ||
| 126 | NpadJoyColorState joycon_color{}; | ||
| 127 | Lifo<NPadGenericState, HidEntryCount> fullkey_lifo{}; | ||
| 128 | Lifo<NPadGenericState, HidEntryCount> handheld_lifo{}; | ||
| 129 | Lifo<NPadGenericState, HidEntryCount> joy_dual_lifo{}; | ||
| 130 | Lifo<NPadGenericState, HidEntryCount> joy_left_lifo{}; | ||
| 131 | Lifo<NPadGenericState, HidEntryCount> joy_right_lifo{}; | ||
| 132 | Lifo<NPadGenericState, HidEntryCount> palma_lifo{}; | ||
| 133 | Lifo<NPadGenericState, HidEntryCount> system_ext_lifo{}; | ||
| 134 | NpadSixAxisSensorLifo sixaxis_fullkey_lifo{}; | ||
| 135 | NpadSixAxisSensorLifo sixaxis_handheld_lifo{}; | ||
| 136 | NpadSixAxisSensorLifo sixaxis_dual_left_lifo{}; | ||
| 137 | NpadSixAxisSensorLifo sixaxis_dual_right_lifo{}; | ||
| 138 | NpadSixAxisSensorLifo sixaxis_left_lifo{}; | ||
| 139 | NpadSixAxisSensorLifo sixaxis_right_lifo{}; | ||
| 140 | DeviceType device_type{}; | ||
| 141 | INSERT_PADDING_BYTES(0x4); // Reserved | ||
| 142 | NPadSystemProperties system_properties{}; | ||
| 143 | NpadSystemButtonProperties button_properties{}; | ||
| 144 | Core::HID::NpadBatteryLevel battery_level_dual{}; | ||
| 145 | Core::HID::NpadBatteryLevel battery_level_left{}; | ||
| 146 | Core::HID::NpadBatteryLevel battery_level_right{}; | ||
| 147 | AppletFooterUiAttributes applet_footer_attributes{}; | ||
| 148 | AppletFooterUiType applet_footer_type{AppletFooterUiType::None}; | ||
| 149 | INSERT_PADDING_BYTES(0x5B); // Reserved | ||
| 150 | INSERT_PADDING_BYTES(0x20); // Unknown | ||
| 151 | Lifo<NpadGcTriggerState, HidEntryCount> gc_trigger_lifo{}; | ||
| 152 | NpadLarkType lark_type_l_and_main{}; | ||
| 153 | NpadLarkType lark_type_r{}; | ||
| 154 | NpadLuciaType lucia_type{}; | ||
| 155 | NpadLagerType lager_type{}; | ||
| 156 | Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties; | ||
| 157 | Core::HID::SixAxisSensorProperties sixaxis_handheld_properties; | ||
| 158 | Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties; | ||
| 159 | Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties; | ||
| 160 | Core::HID::SixAxisSensorProperties sixaxis_left_properties; | ||
| 161 | Core::HID::SixAxisSensorProperties sixaxis_right_properties; | ||
| 162 | }; | ||
| 163 | static_assert(sizeof(NpadInternalState) == 0x43F8, "NpadInternalState is an invalid size"); | ||
| 164 | |||
| 165 | // This is nn::hid::detail::NpadSharedMemoryEntry | ||
| 166 | struct NpadSharedMemoryEntry { | ||
| 167 | NpadInternalState internal_state; | ||
| 168 | INSERT_PADDING_BYTES(0xC08); | ||
| 169 | }; | ||
| 170 | static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is an invalid size"); | ||
| 171 | |||
| 172 | // This is nn::hid::detail::NpadSharedMemoryFormat | ||
| 173 | struct NpadSharedMemoryFormat { | ||
| 174 | std::array<NpadSharedMemoryEntry, MaxSupportedNpadIdTypes> npad_entry; | ||
| 175 | }; | ||
| 176 | static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000, | ||
| 177 | "NpadSharedMemoryFormat is an invalid size"); | ||
| 178 | |||
| 179 | // This is nn::hid::detail::GestureSharedMemoryFormat | ||
| 180 | struct GestureSharedMemoryFormat { | ||
| 181 | // This is nn::hid::detail::GestureLifo | ||
| 182 | Lifo<GestureState, HidEntryCount> gesture_lifo{}; | ||
| 183 | static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size"); | ||
| 184 | INSERT_PADDING_WORDS(0x3E); | ||
| 185 | }; | ||
| 186 | static_assert(sizeof(GestureSharedMemoryFormat) == 0x800, | ||
| 187 | "GestureSharedMemoryFormat is an invalid size"); | ||
| 188 | |||
| 189 | // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat | ||
| 190 | struct ConsoleSixAxisSensorSharedMemoryFormat { | ||
| 191 | u64 sampling_number{}; | ||
| 192 | bool is_seven_six_axis_sensor_at_rest{}; | ||
| 193 | INSERT_PADDING_BYTES(3); // padding | ||
| 194 | f32 verticalization_error{}; | ||
| 195 | Common::Vec3f gyro_bias{}; | ||
| 196 | INSERT_PADDING_BYTES(4); // padding | ||
| 197 | }; | ||
| 198 | static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20, | ||
| 199 | "ConsoleSixAxisSensorSharedMemoryFormat is an invalid size"); | ||
| 200 | |||
| 201 | // This is nn::hid::detail::SharedMemoryFormat | ||
| 202 | struct SharedMemoryFormat { | ||
| 203 | void Initialize() {} | ||
| 204 | |||
| 205 | DebugPadSharedMemoryFormat debug_pad; | ||
| 206 | TouchScreenSharedMemoryFormat touch_screen; | ||
| 207 | MouseSharedMemoryFormat mouse; | ||
| 208 | KeyboardSharedMemoryFormat keyboard; | ||
| 209 | DigitizerSharedMemoryFormat digitizer; | ||
| 210 | HomeButtonSharedMemoryFormat home_button; | ||
| 211 | SleepButtonSharedMemoryFormat sleep_button; | ||
| 212 | CaptureButtonSharedMemoryFormat capture_button; | ||
| 213 | InputDetectorSharedMemoryFormat input_detector; | ||
| 214 | UniquePadSharedMemoryFormat unique_pad; | ||
| 215 | NpadSharedMemoryFormat npad; | ||
| 216 | GestureSharedMemoryFormat gesture; | ||
| 217 | ConsoleSixAxisSensorSharedMemoryFormat console; | ||
| 218 | INSERT_PADDING_BYTES(0x19E0); | ||
| 219 | MouseSharedMemoryFormat debug_mouse; | ||
| 220 | INSERT_PADDING_BYTES(0x2000); | ||
| 221 | }; | ||
| 222 | static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset"); | ||
| 223 | static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset"); | ||
| 224 | static_assert(offsetof(SharedMemoryFormat, mouse) == 0x3400, "mouse has wrong offset"); | ||
| 225 | static_assert(offsetof(SharedMemoryFormat, keyboard) == 0x3800, "keyboard has wrong offset"); | ||
| 226 | static_assert(offsetof(SharedMemoryFormat, digitizer) == 0x3C00, "digitizer has wrong offset"); | ||
| 227 | static_assert(offsetof(SharedMemoryFormat, home_button) == 0x4C00, "home_button has wrong offset"); | ||
| 228 | static_assert(offsetof(SharedMemoryFormat, sleep_button) == 0x4E00, | ||
| 229 | "sleep_button has wrong offset"); | ||
| 230 | static_assert(offsetof(SharedMemoryFormat, capture_button) == 0x5000, | ||
| 231 | "capture_button has wrong offset"); | ||
| 232 | static_assert(offsetof(SharedMemoryFormat, input_detector) == 0x5200, | ||
| 233 | "input_detector has wrong offset"); | ||
| 234 | static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offset"); | ||
| 235 | static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset"); | ||
| 236 | static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset"); | ||
| 237 | static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset"); | ||
| 238 | static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size"); | ||
| 239 | |||
| 240 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/shared_memory_holder.cpp b/src/hid_core/resources/shared_memory_holder.cpp new file mode 100644 index 000000000..ada593d8b --- /dev/null +++ b/src/hid_core/resources/shared_memory_holder.cpp | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 6 | #include "hid_core/hid_result.h" | ||
| 7 | #include "hid_core/resources/applet_resource.h" | ||
| 8 | #include "hid_core/resources/shared_memory_format.h" | ||
| 9 | #include "hid_core/resources/shared_memory_holder.h" | ||
| 10 | |||
| 11 | namespace Service::HID { | ||
| 12 | SharedMemoryHolder::SharedMemoryHolder() {} | ||
| 13 | |||
| 14 | SharedMemoryHolder::~SharedMemoryHolder() { | ||
| 15 | Finalize(); | ||
| 16 | } | ||
| 17 | |||
| 18 | Result SharedMemoryHolder::Initialize(Core::System& system) { | ||
| 19 | shared_memory = Kernel::KSharedMemory::Create(system.Kernel()); | ||
| 20 | const Result result = shared_memory->Initialize( | ||
| 21 | system.DeviceMemory(), nullptr, Kernel::Svc::MemoryPermission::None, | ||
| 22 | Kernel::Svc::MemoryPermission::Read, sizeof(SharedMemoryFormat)); | ||
| 23 | if (result.IsError()) { | ||
| 24 | return result; | ||
| 25 | } | ||
| 26 | Kernel::KSharedMemory::Register(system.Kernel(), shared_memory); | ||
| 27 | |||
| 28 | is_created = true; | ||
| 29 | is_mapped = true; | ||
| 30 | address = std::construct_at(reinterpret_cast<SharedMemoryFormat*>(shared_memory->GetPointer())); | ||
| 31 | return ResultSuccess; | ||
| 32 | } | ||
| 33 | |||
| 34 | void SharedMemoryHolder::Finalize() { | ||
| 35 | if (address != nullptr) { | ||
| 36 | shared_memory->Close(); | ||
| 37 | } | ||
| 38 | is_created = false; | ||
| 39 | is_mapped = false; | ||
| 40 | address = nullptr; | ||
| 41 | } | ||
| 42 | |||
| 43 | bool SharedMemoryHolder::IsMapped() { | ||
| 44 | return is_mapped; | ||
| 45 | } | ||
| 46 | |||
| 47 | SharedMemoryFormat* SharedMemoryHolder::GetAddress() { | ||
| 48 | return address; | ||
| 49 | } | ||
| 50 | |||
| 51 | Kernel::KSharedMemory* SharedMemoryHolder::GetHandle() { | ||
| 52 | return shared_memory; | ||
| 53 | } | ||
| 54 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/shared_memory_holder.h b/src/hid_core/resources/shared_memory_holder.h new file mode 100644 index 000000000..943407c00 --- /dev/null +++ b/src/hid_core/resources/shared_memory_holder.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/result.h" | ||
| 7 | |||
| 8 | namespace Core { | ||
| 9 | class System; | ||
| 10 | } | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | class KSharedMemory; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Service::HID { | ||
| 17 | struct SharedMemoryFormat; | ||
| 18 | |||
| 19 | // This is nn::hid::detail::SharedMemoryHolder | ||
| 20 | class SharedMemoryHolder { | ||
| 21 | public: | ||
| 22 | SharedMemoryHolder(); | ||
| 23 | ~SharedMemoryHolder(); | ||
| 24 | |||
| 25 | Result Initialize(Core::System& system); | ||
| 26 | void Finalize(); | ||
| 27 | |||
| 28 | bool IsMapped(); | ||
| 29 | SharedMemoryFormat* GetAddress(); | ||
| 30 | Kernel::KSharedMemory* GetHandle(); | ||
| 31 | |||
| 32 | private: | ||
| 33 | bool is_owner{}; | ||
| 34 | bool is_created{}; | ||
| 35 | bool is_mapped{}; | ||
| 36 | INSERT_PADDING_BYTES(0x5); | ||
| 37 | Kernel::KSharedMemory* shared_memory; | ||
| 38 | INSERT_PADDING_BYTES(0x38); | ||
| 39 | SharedMemoryFormat* address = nullptr; | ||
| 40 | }; | ||
| 41 | // Correct size is 0x50 bytes | ||
| 42 | static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size"); | ||
| 43 | |||
| 44 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/six_axis/console_six_axis.cpp b/src/hid_core/resources/six_axis/console_six_axis.cpp new file mode 100644 index 000000000..4f733cc76 --- /dev/null +++ b/src/hid_core/resources/six_axis/console_six_axis.cpp | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "hid_core/frontend/emulated_console.h" | ||
| 6 | #include "hid_core/hid_core.h" | ||
| 7 | #include "hid_core/resources/shared_memory_format.h" | ||
| 8 | #include "hid_core/resources/six_axis/console_six_axis.h" | ||
| 9 | |||
| 10 | namespace Service::HID { | ||
| 11 | |||
| 12 | ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { | ||
| 13 | console = hid_core.GetEmulatedConsole(); | ||
| 14 | } | ||
| 15 | |||
| 16 | ConsoleSixAxis::~ConsoleSixAxis() = default; | ||
| 17 | |||
| 18 | void ConsoleSixAxis::OnInit() {} | ||
| 19 | |||
| 20 | void ConsoleSixAxis::OnRelease() {} | ||
| 21 | |||
| 22 | void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 23 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 24 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 25 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 26 | |||
| 27 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | |||
| 31 | ConsoleSixAxisSensorSharedMemoryFormat& shared_memory = data->shared_memory_format->console; | ||
| 32 | |||
| 33 | if (!IsControllerActivated()) { | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | |||
| 37 | const auto motion_status = console->GetMotion(); | ||
| 38 | |||
| 39 | shared_memory.sampling_number++; | ||
| 40 | shared_memory.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; | ||
| 41 | shared_memory.verticalization_error = motion_status.verticalization_error; | ||
| 42 | shared_memory.gyro_bias = motion_status.gyro_bias; | ||
| 43 | } | ||
| 44 | |||
| 45 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/six_axis/console_six_axis.h b/src/hid_core/resources/six_axis/console_six_axis.h new file mode 100644 index 000000000..013b2e93b --- /dev/null +++ b/src/hid_core/resources/six_axis/console_six_axis.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/resources/controller_base.h" | ||
| 7 | |||
| 8 | namespace Core::HID { | ||
| 9 | class EmulatedConsole; | ||
| 10 | } // namespace Core::HID | ||
| 11 | |||
| 12 | namespace Service::HID { | ||
| 13 | class ConsoleSixAxis final : public ControllerBase { | ||
| 14 | public: | ||
| 15 | explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_); | ||
| 16 | ~ConsoleSixAxis() override; | ||
| 17 | |||
| 18 | // Called when the controller is initialized | ||
| 19 | void OnInit() override; | ||
| 20 | |||
| 21 | // When the controller is released | ||
| 22 | void OnRelease() override; | ||
| 23 | |||
| 24 | // When the controller is requesting an update for the shared memory | ||
| 25 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 29 | }; | ||
| 30 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/six_axis/seven_six_axis.cpp b/src/hid_core/resources/six_axis/seven_six_axis.cpp new file mode 100644 index 000000000..d84ef31e1 --- /dev/null +++ b/src/hid_core/resources/six_axis/seven_six_axis.cpp | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include <cstring> | ||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 8 | #include "core/frontend/emu_window.h" | ||
| 9 | #include "core/memory.h" | ||
| 10 | #include "hid_core/frontend/emulated_console.h" | ||
| 11 | #include "hid_core/frontend/emulated_devices.h" | ||
| 12 | #include "hid_core/hid_core.h" | ||
| 13 | #include "hid_core/resources/six_axis/seven_six_axis.h" | ||
| 14 | |||
| 15 | namespace Service::HID { | ||
| 16 | SevenSixAxis::SevenSixAxis(Core::System& system_) | ||
| 17 | : ControllerBase{system_.HIDCore()}, system{system_} { | ||
| 18 | console = hid_core.GetEmulatedConsole(); | ||
| 19 | } | ||
| 20 | |||
| 21 | SevenSixAxis::~SevenSixAxis() = default; | ||
| 22 | |||
| 23 | void SevenSixAxis::OnInit() {} | ||
| 24 | void SevenSixAxis::OnRelease() {} | ||
| 25 | |||
| 26 | void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 27 | if (!IsControllerActivated() || transfer_memory == 0) { | ||
| 28 | seven_sixaxis_lifo.buffer_count = 0; | ||
| 29 | seven_sixaxis_lifo.buffer_tail = 0; | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | |||
| 33 | const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state; | ||
| 34 | next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1; | ||
| 35 | |||
| 36 | const auto motion_status = console->GetMotion(); | ||
| 37 | last_global_timestamp = core_timing.GetGlobalTimeNs().count(); | ||
| 38 | |||
| 39 | // This value increments every time the switch goes to sleep | ||
| 40 | next_seven_sixaxis_state.unknown = 1; | ||
| 41 | next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp; | ||
| 42 | next_seven_sixaxis_state.accel = motion_status.accel; | ||
| 43 | next_seven_sixaxis_state.gyro = motion_status.gyro; | ||
| 44 | next_seven_sixaxis_state.quaternion = { | ||
| 45 | { | ||
| 46 | motion_status.quaternion.xyz.y, | ||
| 47 | motion_status.quaternion.xyz.x, | ||
| 48 | -motion_status.quaternion.w, | ||
| 49 | }, | ||
| 50 | -motion_status.quaternion.xyz.z, | ||
| 51 | }; | ||
| 52 | |||
| 53 | seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); | ||
| 54 | system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, | ||
| 55 | sizeof(seven_sixaxis_lifo)); | ||
| 56 | } | ||
| 57 | |||
| 58 | void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { | ||
| 59 | transfer_memory = t_mem; | ||
| 60 | } | ||
| 61 | |||
| 62 | void SevenSixAxis::ResetTimestamp() { | ||
| 63 | last_saved_timestamp = last_global_timestamp; | ||
| 64 | } | ||
| 65 | |||
| 66 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/six_axis/seven_six_axis.h b/src/hid_core/resources/six_axis/seven_six_axis.h new file mode 100644 index 000000000..0a26c77c9 --- /dev/null +++ b/src/hid_core/resources/six_axis/seven_six_axis.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "common/quaternion.h" | ||
| 8 | #include "common/typed_address.h" | ||
| 9 | #include "hid_core/resources/controller_base.h" | ||
| 10 | #include "hid_core/resources/ring_lifo.h" | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } // namespace Core | ||
| 15 | |||
| 16 | namespace Core::HID { | ||
| 17 | class EmulatedConsole; | ||
| 18 | } // namespace Core::HID | ||
| 19 | |||
| 20 | namespace Service::HID { | ||
| 21 | class SevenSixAxis final : public ControllerBase { | ||
| 22 | public: | ||
| 23 | explicit SevenSixAxis(Core::System& system_); | ||
| 24 | ~SevenSixAxis() override; | ||
| 25 | |||
| 26 | // Called when the controller is initialized | ||
| 27 | void OnInit() override; | ||
| 28 | |||
| 29 | // When the controller is released | ||
| 30 | void OnRelease() override; | ||
| 31 | |||
| 32 | // When the controller is requesting an update for the shared memory | ||
| 33 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 34 | |||
| 35 | // Called on InitializeSevenSixAxisSensor | ||
| 36 | void SetTransferMemoryAddress(Common::ProcessAddress t_mem); | ||
| 37 | |||
| 38 | // Called on ResetSevenSixAxisSensorTimestamp | ||
| 39 | void ResetTimestamp(); | ||
| 40 | |||
| 41 | private: | ||
| 42 | struct SevenSixAxisState { | ||
| 43 | INSERT_PADDING_WORDS(2); // unused | ||
| 44 | u64 timestamp{}; | ||
| 45 | u64 sampling_number{}; | ||
| 46 | u64 unknown{}; | ||
| 47 | Common::Vec3f accel{}; | ||
| 48 | Common::Vec3f gyro{}; | ||
| 49 | Common::Quaternion<f32> quaternion{}; | ||
| 50 | }; | ||
| 51 | static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); | ||
| 52 | |||
| 53 | Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; | ||
| 54 | static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); | ||
| 55 | |||
| 56 | u64 last_saved_timestamp{}; | ||
| 57 | u64 last_global_timestamp{}; | ||
| 58 | |||
| 59 | SevenSixAxisState next_seven_sixaxis_state{}; | ||
| 60 | Common::ProcessAddress transfer_memory{}; | ||
| 61 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 62 | |||
| 63 | Core::System& system; | ||
| 64 | }; | ||
| 65 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/six_axis/six_axis.cpp b/src/hid_core/resources/six_axis/six_axis.cpp new file mode 100644 index 000000000..8a9677c50 --- /dev/null +++ b/src/hid_core/resources/six_axis/six_axis.cpp | |||
| @@ -0,0 +1,421 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/common_types.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "hid_core/frontend/emulated_controller.h" | ||
| 7 | #include "hid_core/hid_core.h" | ||
| 8 | #include "hid_core/hid_result.h" | ||
| 9 | #include "hid_core/hid_util.h" | ||
| 10 | #include "hid_core/resources/npad/npad.h" | ||
| 11 | #include "hid_core/resources/shared_memory_format.h" | ||
| 12 | #include "hid_core/resources/six_axis/six_axis.h" | ||
| 13 | |||
| 14 | namespace Service::HID { | ||
| 15 | |||
| 16 | SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_) | ||
| 17 | : ControllerBase{hid_core_}, npad{npad_} { | ||
| 18 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | ||
| 19 | auto& controller = controller_data[i]; | ||
| 20 | controller.device = hid_core.GetEmulatedControllerByIndex(i); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | SixAxis::~SixAxis() = default; | ||
| 25 | |||
| 26 | void SixAxis::OnInit() {} | ||
| 27 | void SixAxis::OnRelease() {} | ||
| 28 | |||
| 29 | void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 30 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 31 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 32 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 33 | |||
| 34 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 35 | return; | ||
| 36 | } | ||
| 37 | |||
| 38 | if (!IsControllerActivated()) { | ||
| 39 | return; | ||
| 40 | } | ||
| 41 | |||
| 42 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | ||
| 43 | NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i]; | ||
| 44 | auto& controller = controller_data[i]; | ||
| 45 | const auto& controller_type = controller.device->GetNpadStyleIndex(); | ||
| 46 | |||
| 47 | if (controller_type == Core::HID::NpadStyleIndex::None || | ||
| 48 | !controller.device->IsConnected()) { | ||
| 49 | continue; | ||
| 50 | } | ||
| 51 | |||
| 52 | const auto& motion_state = controller.device->GetMotions(); | ||
| 53 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | ||
| 54 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; | ||
| 55 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; | ||
| 56 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; | ||
| 57 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; | ||
| 58 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; | ||
| 59 | |||
| 60 | auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo; | ||
| 61 | auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo; | ||
| 62 | auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo; | ||
| 63 | auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo; | ||
| 64 | auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo; | ||
| 65 | auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo; | ||
| 66 | |||
| 67 | // Clear previous state | ||
| 68 | sixaxis_fullkey_state = {}; | ||
| 69 | sixaxis_handheld_state = {}; | ||
| 70 | sixaxis_dual_left_state = {}; | ||
| 71 | sixaxis_dual_right_state = {}; | ||
| 72 | sixaxis_left_lifo_state = {}; | ||
| 73 | sixaxis_right_lifo_state = {}; | ||
| 74 | |||
| 75 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { | ||
| 76 | controller.sixaxis_at_rest = true; | ||
| 77 | for (std::size_t e = 0; e < motion_state.size(); ++e) { | ||
| 78 | controller.sixaxis_at_rest = | ||
| 79 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, | ||
| 84 | const Core::HID::ControllerMotion& hid_state) { | ||
| 85 | using namespace std::literals::chrono_literals; | ||
| 86 | static constexpr Core::HID::SixAxisSensorState default_motion_state = { | ||
| 87 | .delta_time = std::chrono::nanoseconds(5ms).count(), | ||
| 88 | .accel = {0, 0, -1.0f}, | ||
| 89 | .orientation = | ||
| 90 | { | ||
| 91 | Common::Vec3f{1.0f, 0, 0}, | ||
| 92 | Common::Vec3f{0, 1.0f, 0}, | ||
| 93 | Common::Vec3f{0, 0, 1.0f}, | ||
| 94 | }, | ||
| 95 | .attribute = {1}, | ||
| 96 | }; | ||
| 97 | if (!controller.sixaxis_sensor_enabled) { | ||
| 98 | state = default_motion_state; | ||
| 99 | return; | ||
| 100 | } | ||
| 101 | if (!Settings::values.motion_enabled.GetValue()) { | ||
| 102 | state = default_motion_state; | ||
| 103 | return; | ||
| 104 | } | ||
| 105 | state.attribute.is_connected.Assign(1); | ||
| 106 | state.delta_time = std::chrono::nanoseconds(5ms).count(); | ||
| 107 | state.accel = hid_state.accel; | ||
| 108 | state.gyro = hid_state.gyro; | ||
| 109 | state.rotation = hid_state.rotation; | ||
| 110 | state.orientation = hid_state.orientation; | ||
| 111 | }; | ||
| 112 | |||
| 113 | switch (controller_type) { | ||
| 114 | case Core::HID::NpadStyleIndex::None: | ||
| 115 | ASSERT(false); | ||
| 116 | break; | ||
| 117 | case Core::HID::NpadStyleIndex::ProController: | ||
| 118 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 119 | break; | ||
| 120 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 121 | set_motion_state(sixaxis_handheld_state, motion_state[0]); | ||
| 122 | break; | ||
| 123 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 124 | set_motion_state(sixaxis_dual_left_state, motion_state[0]); | ||
| 125 | set_motion_state(sixaxis_dual_right_state, motion_state[1]); | ||
| 126 | break; | ||
| 127 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 128 | set_motion_state(sixaxis_left_lifo_state, motion_state[0]); | ||
| 129 | break; | ||
| 130 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 131 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | ||
| 132 | break; | ||
| 133 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 134 | using namespace std::literals::chrono_literals; | ||
| 135 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 136 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 137 | break; | ||
| 138 | default: | ||
| 139 | break; | ||
| 140 | } | ||
| 141 | |||
| 142 | sixaxis_fullkey_state.sampling_number = | ||
| 143 | sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 144 | sixaxis_handheld_state.sampling_number = | ||
| 145 | sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 146 | sixaxis_dual_left_state.sampling_number = | ||
| 147 | sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 148 | sixaxis_dual_right_state.sampling_number = | ||
| 149 | sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 150 | sixaxis_left_lifo_state.sampling_number = | ||
| 151 | sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 152 | sixaxis_right_lifo_state.sampling_number = | ||
| 153 | sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 154 | |||
| 155 | if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 156 | // This buffer only is updated on handheld on HW | ||
| 157 | sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 158 | } else { | ||
| 159 | // Handheld doesn't update this buffer on HW | ||
| 160 | sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 161 | } | ||
| 162 | |||
| 163 | sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state); | ||
| 164 | sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state); | ||
| 165 | sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state); | ||
| 166 | sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 171 | Core::HID::GyroscopeZeroDriftMode drift_mode) { | ||
| 172 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 173 | if (is_valid.IsError()) { | ||
| 174 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 175 | return is_valid; | ||
| 176 | } | ||
| 177 | |||
| 178 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 179 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 180 | sixaxis.gyroscope_zero_drift_mode = drift_mode; | ||
| 181 | controller.device->SetGyroscopeZeroDriftMode(drift_mode); | ||
| 182 | |||
| 183 | return ResultSuccess; | ||
| 184 | } | ||
| 185 | |||
| 186 | Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 187 | Core::HID::GyroscopeZeroDriftMode& drift_mode) const { | ||
| 188 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 189 | if (is_valid.IsError()) { | ||
| 190 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 191 | return is_valid; | ||
| 192 | } | ||
| 193 | |||
| 194 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 195 | drift_mode = sixaxis.gyroscope_zero_drift_mode; | ||
| 196 | |||
| 197 | return ResultSuccess; | ||
| 198 | } | ||
| 199 | |||
| 200 | Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 201 | bool& is_at_rest) const { | ||
| 202 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 203 | if (is_valid.IsError()) { | ||
| 204 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 205 | return is_valid; | ||
| 206 | } | ||
| 207 | |||
| 208 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 209 | is_at_rest = controller.sixaxis_at_rest; | ||
| 210 | return ResultSuccess; | ||
| 211 | } | ||
| 212 | |||
| 213 | Result SixAxis::LoadSixAxisSensorCalibrationParameter( | ||
| 214 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 215 | Core::HID::SixAxisSensorCalibrationParameter& calibration) const { | ||
| 216 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 217 | if (is_valid.IsError()) { | ||
| 218 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 219 | return is_valid; | ||
| 220 | } | ||
| 221 | |||
| 222 | // TODO: Request this data to the controller. On error return 0xd8ca | ||
| 223 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 224 | calibration = sixaxis.calibration; | ||
| 225 | return ResultSuccess; | ||
| 226 | } | ||
| 227 | |||
| 228 | Result SixAxis::GetSixAxisSensorIcInformation( | ||
| 229 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 230 | Core::HID::SixAxisSensorIcInformation& ic_information) const { | ||
| 231 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 232 | if (is_valid.IsError()) { | ||
| 233 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 234 | return is_valid; | ||
| 235 | } | ||
| 236 | |||
| 237 | // TODO: Request this data to the controller. On error return 0xd8ca | ||
| 238 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 239 | ic_information = sixaxis.ic_information; | ||
| 240 | return ResultSuccess; | ||
| 241 | } | ||
| 242 | |||
| 243 | Result SixAxis::EnableSixAxisSensorUnalteredPassthrough( | ||
| 244 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { | ||
| 245 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 246 | if (is_valid.IsError()) { | ||
| 247 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 248 | return is_valid; | ||
| 249 | } | ||
| 250 | |||
| 251 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 252 | sixaxis.unaltered_passtrough = is_enabled; | ||
| 253 | return ResultSuccess; | ||
| 254 | } | ||
| 255 | |||
| 256 | Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 257 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { | ||
| 258 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 259 | if (is_valid.IsError()) { | ||
| 260 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 261 | return is_valid; | ||
| 262 | } | ||
| 263 | |||
| 264 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 265 | is_enabled = sixaxis.unaltered_passtrough; | ||
| 266 | return ResultSuccess; | ||
| 267 | } | ||
| 268 | |||
| 269 | Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 270 | bool sixaxis_status) { | ||
| 271 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 272 | if (is_valid.IsError()) { | ||
| 273 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 274 | return is_valid; | ||
| 275 | } | ||
| 276 | |||
| 277 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 278 | controller.sixaxis_sensor_enabled = sixaxis_status; | ||
| 279 | return ResultSuccess; | ||
| 280 | } | ||
| 281 | |||
| 282 | Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 283 | bool& is_fusion_enabled) const { | ||
| 284 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 285 | if (is_valid.IsError()) { | ||
| 286 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 287 | return is_valid; | ||
| 288 | } | ||
| 289 | |||
| 290 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 291 | is_fusion_enabled = sixaxis.is_fusion_enabled; | ||
| 292 | |||
| 293 | return ResultSuccess; | ||
| 294 | } | ||
| 295 | Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 296 | bool is_fusion_enabled) { | ||
| 297 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 298 | if (is_valid.IsError()) { | ||
| 299 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 300 | return is_valid; | ||
| 301 | } | ||
| 302 | |||
| 303 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 304 | sixaxis.is_fusion_enabled = is_fusion_enabled; | ||
| 305 | |||
| 306 | return ResultSuccess; | ||
| 307 | } | ||
| 308 | |||
| 309 | Result SixAxis::SetSixAxisFusionParameters( | ||
| 310 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 311 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { | ||
| 312 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 313 | if (is_valid.IsError()) { | ||
| 314 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 315 | return is_valid; | ||
| 316 | } | ||
| 317 | |||
| 318 | const auto param1 = sixaxis_fusion_parameters.parameter1; | ||
| 319 | if (param1 < 0.0f || param1 > 1.0f) { | ||
| 320 | return InvalidSixAxisFusionRange; | ||
| 321 | } | ||
| 322 | |||
| 323 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 324 | sixaxis.fusion = sixaxis_fusion_parameters; | ||
| 325 | |||
| 326 | return ResultSuccess; | ||
| 327 | } | ||
| 328 | |||
| 329 | Result SixAxis::GetSixAxisFusionParameters( | ||
| 330 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 331 | Core::HID::SixAxisSensorFusionParameters& parameters) const { | ||
| 332 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 333 | if (is_valid.IsError()) { | ||
| 334 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 335 | return is_valid; | ||
| 336 | } | ||
| 337 | |||
| 338 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 339 | parameters = sixaxis.fusion; | ||
| 340 | |||
| 341 | return ResultSuccess; | ||
| 342 | } | ||
| 343 | |||
| 344 | SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( | ||
| 345 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | ||
| 346 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 347 | switch (sixaxis_handle.npad_type) { | ||
| 348 | case Core::HID::NpadStyleIndex::ProController: | ||
| 349 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 350 | return controller.sixaxis_fullkey; | ||
| 351 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 352 | return controller.sixaxis_handheld; | ||
| 353 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 354 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 355 | return controller.sixaxis_dual_left; | ||
| 356 | } | ||
| 357 | return controller.sixaxis_dual_right; | ||
| 358 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 359 | return controller.sixaxis_left; | ||
| 360 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 361 | return controller.sixaxis_right; | ||
| 362 | default: | ||
| 363 | return controller.sixaxis_unknown; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( | ||
| 368 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { | ||
| 369 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 370 | switch (sixaxis_handle.npad_type) { | ||
| 371 | case Core::HID::NpadStyleIndex::ProController: | ||
| 372 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 373 | return controller.sixaxis_fullkey; | ||
| 374 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 375 | return controller.sixaxis_handheld; | ||
| 376 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 377 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 378 | return controller.sixaxis_dual_left; | ||
| 379 | } | ||
| 380 | return controller.sixaxis_dual_right; | ||
| 381 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 382 | return controller.sixaxis_left; | ||
| 383 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 384 | return controller.sixaxis_right; | ||
| 385 | default: | ||
| 386 | return controller.sixaxis_unknown; | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle( | ||
| 391 | const Core::HID::SixAxisSensorHandle& device_handle) { | ||
| 392 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 393 | return GetControllerFromNpadIdType(npad_id); | ||
| 394 | } | ||
| 395 | |||
| 396 | const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle( | ||
| 397 | const Core::HID::SixAxisSensorHandle& device_handle) const { | ||
| 398 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 399 | return GetControllerFromNpadIdType(npad_id); | ||
| 400 | } | ||
| 401 | |||
| 402 | SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { | ||
| 403 | if (!IsNpadIdValid(npad_id)) { | ||
| 404 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 405 | npad_id = Core::HID::NpadIdType::Player1; | ||
| 406 | } | ||
| 407 | const auto npad_index = NpadIdTypeToIndex(npad_id); | ||
| 408 | return controller_data[npad_index]; | ||
| 409 | } | ||
| 410 | |||
| 411 | const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType( | ||
| 412 | Core::HID::NpadIdType npad_id) const { | ||
| 413 | if (!IsNpadIdValid(npad_id)) { | ||
| 414 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 415 | npad_id = Core::HID::NpadIdType::Player1; | ||
| 416 | } | ||
| 417 | const auto npad_index = NpadIdTypeToIndex(npad_id); | ||
| 418 | return controller_data[npad_index]; | ||
| 419 | } | ||
| 420 | |||
| 421 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/six_axis/six_axis.h b/src/hid_core/resources/six_axis/six_axis.h new file mode 100644 index 000000000..1054e1b27 --- /dev/null +++ b/src/hid_core/resources/six_axis/six_axis.h | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "hid_core/hid_types.h" | ||
| 8 | #include "hid_core/resources/controller_base.h" | ||
| 9 | #include "hid_core/resources/ring_lifo.h" | ||
| 10 | |||
| 11 | namespace Core::HID { | ||
| 12 | class EmulatedController; | ||
| 13 | } // namespace Core::HID | ||
| 14 | |||
| 15 | namespace Service::HID { | ||
| 16 | class NPad; | ||
| 17 | |||
| 18 | class SixAxis final : public ControllerBase { | ||
| 19 | public: | ||
| 20 | explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_); | ||
| 21 | ~SixAxis() override; | ||
| 22 | |||
| 23 | // Called when the controller is initialized | ||
| 24 | void OnInit() override; | ||
| 25 | |||
| 26 | // When the controller is released | ||
| 27 | void OnRelease() override; | ||
| 28 | |||
| 29 | // When the controller is requesting an update for the shared memory | ||
| 30 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 31 | |||
| 32 | Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 33 | Core::HID::GyroscopeZeroDriftMode drift_mode); | ||
| 34 | Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 35 | Core::HID::GyroscopeZeroDriftMode& drift_mode) const; | ||
| 36 | Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 37 | bool& is_at_rest) const; | ||
| 38 | Result EnableSixAxisSensorUnalteredPassthrough( | ||
| 39 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); | ||
| 40 | Result IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 41 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; | ||
| 42 | Result LoadSixAxisSensorCalibrationParameter( | ||
| 43 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 44 | Core::HID::SixAxisSensorCalibrationParameter& calibration) const; | ||
| 45 | Result GetSixAxisSensorIcInformation( | ||
| 46 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 47 | Core::HID::SixAxisSensorIcInformation& ic_information) const; | ||
| 48 | Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 49 | bool sixaxis_status); | ||
| 50 | Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 51 | bool& is_fusion_enabled) const; | ||
| 52 | Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 53 | bool is_fusion_enabled); | ||
| 54 | Result SetSixAxisFusionParameters( | ||
| 55 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 56 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); | ||
| 57 | Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 58 | Core::HID::SixAxisSensorFusionParameters& parameters) const; | ||
| 59 | |||
| 60 | private: | ||
| 61 | static constexpr std::size_t NPAD_COUNT = 10; | ||
| 62 | |||
| 63 | struct SixaxisParameters { | ||
| 64 | bool is_fusion_enabled{true}; | ||
| 65 | bool unaltered_passtrough{false}; | ||
| 66 | Core::HID::SixAxisSensorFusionParameters fusion{}; | ||
| 67 | Core::HID::SixAxisSensorCalibrationParameter calibration{}; | ||
| 68 | Core::HID::SixAxisSensorIcInformation ic_information{}; | ||
| 69 | Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{ | ||
| 70 | Core::HID::GyroscopeZeroDriftMode::Standard}; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct NpadControllerData { | ||
| 74 | Core::HID::EmulatedController* device = nullptr; | ||
| 75 | |||
| 76 | // Motion parameters | ||
| 77 | bool sixaxis_at_rest{true}; | ||
| 78 | bool sixaxis_sensor_enabled{true}; | ||
| 79 | SixaxisParameters sixaxis_fullkey{}; | ||
| 80 | SixaxisParameters sixaxis_handheld{}; | ||
| 81 | SixaxisParameters sixaxis_dual_left{}; | ||
| 82 | SixaxisParameters sixaxis_dual_right{}; | ||
| 83 | SixaxisParameters sixaxis_left{}; | ||
| 84 | SixaxisParameters sixaxis_right{}; | ||
| 85 | SixaxisParameters sixaxis_unknown{}; | ||
| 86 | |||
| 87 | // Current pad state | ||
| 88 | Core::HID::SixAxisSensorState sixaxis_fullkey_state{}; | ||
| 89 | Core::HID::SixAxisSensorState sixaxis_handheld_state{}; | ||
| 90 | Core::HID::SixAxisSensorState sixaxis_dual_left_state{}; | ||
| 91 | Core::HID::SixAxisSensorState sixaxis_dual_right_state{}; | ||
| 92 | Core::HID::SixAxisSensorState sixaxis_left_lifo_state{}; | ||
| 93 | Core::HID::SixAxisSensorState sixaxis_right_lifo_state{}; | ||
| 94 | int callback_key{}; | ||
| 95 | }; | ||
| 96 | |||
| 97 | SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 98 | const SixaxisParameters& GetSixaxisState( | ||
| 99 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 100 | |||
| 101 | NpadControllerData& GetControllerFromHandle( | ||
| 102 | const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 103 | const NpadControllerData& GetControllerFromHandle( | ||
| 104 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 105 | NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); | ||
| 106 | const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; | ||
| 107 | |||
| 108 | std::shared_ptr<NPad> npad; | ||
| 109 | std::array<NpadControllerData, NPAD_COUNT> controller_data{}; | ||
| 110 | }; | ||
| 111 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/system_buttons/capture_button.cpp b/src/hid_core/resources/system_buttons/capture_button.cpp new file mode 100644 index 000000000..70973ae25 --- /dev/null +++ b/src/hid_core/resources/system_buttons/capture_button.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "hid_core/resources/applet_resource.h" | ||
| 6 | #include "hid_core/resources/shared_memory_format.h" | ||
| 7 | #include "hid_core/resources/system_buttons/capture_button.h" | ||
| 8 | |||
| 9 | namespace Service::HID { | ||
| 10 | |||
| 11 | CaptureButton::CaptureButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} | ||
| 12 | |||
| 13 | CaptureButton::~CaptureButton() = default; | ||
| 14 | |||
| 15 | void CaptureButton::OnInit() {} | ||
| 16 | |||
| 17 | void CaptureButton::OnRelease() {} | ||
| 18 | |||
| 19 | void CaptureButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 20 | if (!smart_update) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | |||
| 24 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 25 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 26 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 27 | |||
| 28 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | |||
| 32 | auto& header = data->shared_memory_format->capture_button.header; | ||
| 33 | header.timestamp = core_timing.GetGlobalTimeNs().count(); | ||
| 34 | header.total_entry_count = 17; | ||
| 35 | header.entry_count = 0; | ||
| 36 | header.last_entry_index = 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/system_buttons/capture_button.h b/src/hid_core/resources/system_buttons/capture_button.h new file mode 100644 index 000000000..ad95d7cad --- /dev/null +++ b/src/hid_core/resources/system_buttons/capture_button.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/resources/controller_base.h" | ||
| 7 | |||
| 8 | namespace Service::HID { | ||
| 9 | |||
| 10 | class CaptureButton final : public ControllerBase { | ||
| 11 | public: | ||
| 12 | explicit CaptureButton(Core::HID::HIDCore& hid_core_); | ||
| 13 | ~CaptureButton() override; | ||
| 14 | |||
| 15 | // Called when the controller is initialized | ||
| 16 | void OnInit() override; | ||
| 17 | |||
| 18 | // When the controller is released | ||
| 19 | void OnRelease() override; | ||
| 20 | |||
| 21 | // When the controller is requesting an update for the shared memory | ||
| 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 23 | |||
| 24 | private: | ||
| 25 | bool smart_update{}; | ||
| 26 | }; | ||
| 27 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/system_buttons/home_button.cpp b/src/hid_core/resources/system_buttons/home_button.cpp new file mode 100644 index 000000000..f9c1f44b5 --- /dev/null +++ b/src/hid_core/resources/system_buttons/home_button.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "hid_core/resources/applet_resource.h" | ||
| 6 | #include "hid_core/resources/shared_memory_format.h" | ||
| 7 | #include "hid_core/resources/system_buttons/home_button.h" | ||
| 8 | |||
| 9 | namespace Service::HID { | ||
| 10 | |||
| 11 | HomeButton::HomeButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} | ||
| 12 | |||
| 13 | HomeButton::~HomeButton() = default; | ||
| 14 | |||
| 15 | void HomeButton::OnInit() {} | ||
| 16 | |||
| 17 | void HomeButton::OnRelease() {} | ||
| 18 | |||
| 19 | void HomeButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 20 | if (!smart_update) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | |||
| 24 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 25 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 26 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 27 | |||
| 28 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | |||
| 32 | auto& header = data->shared_memory_format->home_button.header; | ||
| 33 | header.timestamp = core_timing.GetGlobalTimeNs().count(); | ||
| 34 | header.total_entry_count = 17; | ||
| 35 | header.entry_count = 0; | ||
| 36 | header.last_entry_index = 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/system_buttons/home_button.h b/src/hid_core/resources/system_buttons/home_button.h new file mode 100644 index 000000000..ecf8327f4 --- /dev/null +++ b/src/hid_core/resources/system_buttons/home_button.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/resources/controller_base.h" | ||
| 7 | |||
| 8 | namespace Service::HID { | ||
| 9 | |||
| 10 | class HomeButton final : public ControllerBase { | ||
| 11 | public: | ||
| 12 | explicit HomeButton(Core::HID::HIDCore& hid_core_); | ||
| 13 | ~HomeButton() override; | ||
| 14 | |||
| 15 | // Called when the controller is initialized | ||
| 16 | void OnInit() override; | ||
| 17 | |||
| 18 | // When the controller is released | ||
| 19 | void OnRelease() override; | ||
| 20 | |||
| 21 | // When the controller is requesting an update for the shared memory | ||
| 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 23 | |||
| 24 | private: | ||
| 25 | bool smart_update{}; | ||
| 26 | }; | ||
| 27 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/system_buttons/sleep_button.cpp b/src/hid_core/resources/system_buttons/sleep_button.cpp new file mode 100644 index 000000000..22adf501f --- /dev/null +++ b/src/hid_core/resources/system_buttons/sleep_button.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "hid_core/resources/applet_resource.h" | ||
| 6 | #include "hid_core/resources/shared_memory_format.h" | ||
| 7 | #include "hid_core/resources/system_buttons/sleep_button.h" | ||
| 8 | |||
| 9 | namespace Service::HID { | ||
| 10 | |||
| 11 | SleepButton::SleepButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} | ||
| 12 | |||
| 13 | SleepButton::~SleepButton() = default; | ||
| 14 | |||
| 15 | void SleepButton::OnInit() {} | ||
| 16 | |||
| 17 | void SleepButton::OnRelease() {} | ||
| 18 | |||
| 19 | void SleepButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 20 | if (!smart_update) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | |||
| 24 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 25 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 26 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 27 | |||
| 28 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | |||
| 32 | auto& header = data->shared_memory_format->capture_button.header; | ||
| 33 | header.timestamp = core_timing.GetGlobalTimeNs().count(); | ||
| 34 | header.total_entry_count = 17; | ||
| 35 | header.entry_count = 0; | ||
| 36 | header.last_entry_index = 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/system_buttons/sleep_button.h b/src/hid_core/resources/system_buttons/sleep_button.h new file mode 100644 index 000000000..f9ed38c33 --- /dev/null +++ b/src/hid_core/resources/system_buttons/sleep_button.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/resources/controller_base.h" | ||
| 7 | |||
| 8 | namespace Service::HID { | ||
| 9 | |||
| 10 | class SleepButton final : public ControllerBase { | ||
| 11 | public: | ||
| 12 | explicit SleepButton(Core::HID::HIDCore& hid_core_); | ||
| 13 | ~SleepButton() override; | ||
| 14 | |||
| 15 | // Called when the controller is initialized | ||
| 16 | void OnInit() override; | ||
| 17 | |||
| 18 | // When the controller is released | ||
| 19 | void OnRelease() override; | ||
| 20 | |||
| 21 | // When the controller is requesting an update for the shared memory | ||
| 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 23 | |||
| 24 | private: | ||
| 25 | bool smart_update{}; | ||
| 26 | }; | ||
| 27 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/touch_screen/gesture.cpp b/src/hid_core/resources/touch_screen/gesture.cpp new file mode 100644 index 000000000..0ecc0941f --- /dev/null +++ b/src/hid_core/resources/touch_screen/gesture.cpp | |||
| @@ -0,0 +1,366 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/math_util.h" | ||
| 5 | #include "common/settings.h" | ||
| 6 | #include "core/frontend/emu_window.h" | ||
| 7 | #include "hid_core/frontend/emulated_console.h" | ||
| 8 | #include "hid_core/hid_core.h" | ||
| 9 | #include "hid_core/resources/applet_resource.h" | ||
| 10 | #include "hid_core/resources/shared_memory_format.h" | ||
| 11 | #include "hid_core/resources/touch_screen/gesture.h" | ||
| 12 | |||
| 13 | namespace Service::HID { | ||
| 14 | // HW is around 700, value is set to 400 to make it easier to trigger with mouse | ||
| 15 | constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s | ||
| 16 | constexpr f32 angle_threshold = 0.015f; // Threshold in radians | ||
| 17 | constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels | ||
| 18 | constexpr f32 press_delay = 0.5f; // Time in seconds | ||
| 19 | constexpr f32 double_tap_delay = 0.35f; // Time in seconds | ||
| 20 | |||
| 21 | constexpr f32 Square(s32 num) { | ||
| 22 | return static_cast<f32>(num * num); | ||
| 23 | } | ||
| 24 | |||
| 25 | Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { | ||
| 26 | console = hid_core.GetEmulatedConsole(); | ||
| 27 | } | ||
| 28 | Gesture::~Gesture() = default; | ||
| 29 | |||
| 30 | void Gesture::OnInit() { | ||
| 31 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 32 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 33 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 34 | |||
| 35 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | |||
| 39 | shared_memory = &data->shared_memory_format->gesture; | ||
| 40 | shared_memory->gesture_lifo.buffer_count = 0; | ||
| 41 | shared_memory->gesture_lifo.buffer_tail = 0; | ||
| 42 | force_update = true; | ||
| 43 | } | ||
| 44 | |||
| 45 | void Gesture::OnRelease() {} | ||
| 46 | |||
| 47 | void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 48 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 49 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 50 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 51 | |||
| 52 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | |||
| 56 | shared_memory = &data->shared_memory_format->gesture; | ||
| 57 | |||
| 58 | if (!IsControllerActivated()) { | ||
| 59 | shared_memory->gesture_lifo.buffer_count = 0; | ||
| 60 | shared_memory->gesture_lifo.buffer_tail = 0; | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | ReadTouchInput(); | ||
| 65 | |||
| 66 | GestureProperties gesture = GetGestureProperties(); | ||
| 67 | f32 time_difference = | ||
| 68 | static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / | ||
| 69 | (1000 * 1000 * 1000); | ||
| 70 | |||
| 71 | // Only update if necessary | ||
| 72 | if (!ShouldUpdateGesture(gesture, time_difference)) { | ||
| 73 | return; | ||
| 74 | } | ||
| 75 | |||
| 76 | last_update_timestamp = shared_memory->gesture_lifo.timestamp; | ||
| 77 | UpdateGestureSharedMemory(gesture, time_difference); | ||
| 78 | } | ||
| 79 | |||
| 80 | void Gesture::ReadTouchInput() { | ||
| 81 | if (!Settings::values.touchscreen.enabled) { | ||
| 82 | fingers = {}; | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | const auto touch_status = console->GetTouch(); | ||
| 87 | for (std::size_t id = 0; id < fingers.size(); ++id) { | ||
| 88 | fingers[id] = touch_status[id]; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { | ||
| 93 | const auto& last_entry = GetLastGestureEntry(); | ||
| 94 | if (force_update) { | ||
| 95 | force_update = false; | ||
| 96 | return true; | ||
| 97 | } | ||
| 98 | |||
| 99 | // Update if coordinates change | ||
| 100 | for (size_t id = 0; id < MAX_POINTS; id++) { | ||
| 101 | if (gesture.points[id] != last_gesture.points[id]) { | ||
| 102 | return true; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | // Update on press and hold event after 0.5 seconds | ||
| 107 | if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 && | ||
| 108 | time_difference > press_delay) { | ||
| 109 | return enable_press_and_tap; | ||
| 110 | } | ||
| 111 | |||
| 112 | return false; | ||
| 113 | } | ||
| 114 | |||
| 115 | void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { | ||
| 116 | GestureType type = GestureType::Idle; | ||
| 117 | GestureAttribute attributes{}; | ||
| 118 | |||
| 119 | const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; | ||
| 120 | |||
| 121 | // Reset next state to default | ||
| 122 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 123 | next_state.delta = {}; | ||
| 124 | next_state.vel_x = 0; | ||
| 125 | next_state.vel_y = 0; | ||
| 126 | next_state.direction = GestureDirection::None; | ||
| 127 | next_state.rotation_angle = 0; | ||
| 128 | next_state.scale = 0; | ||
| 129 | |||
| 130 | if (gesture.active_points > 0) { | ||
| 131 | if (last_gesture.active_points == 0) { | ||
| 132 | NewGesture(gesture, type, attributes); | ||
| 133 | } else { | ||
| 134 | UpdateExistingGesture(gesture, type, time_difference); | ||
| 135 | } | ||
| 136 | } else { | ||
| 137 | EndGesture(gesture, last_gesture, type, attributes, time_difference); | ||
| 138 | } | ||
| 139 | |||
| 140 | // Apply attributes | ||
| 141 | next_state.detection_count = gesture.detection_count; | ||
| 142 | next_state.type = type; | ||
| 143 | next_state.attributes = attributes; | ||
| 144 | next_state.pos = gesture.mid_point; | ||
| 145 | next_state.point_count = static_cast<s32>(gesture.active_points); | ||
| 146 | next_state.points = gesture.points; | ||
| 147 | last_gesture = gesture; | ||
| 148 | |||
| 149 | shared_memory->gesture_lifo.WriteNextEntry(next_state); | ||
| 150 | } | ||
| 151 | |||
| 152 | void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, | ||
| 153 | GestureAttribute& attributes) { | ||
| 154 | const auto& last_entry = GetLastGestureEntry(); | ||
| 155 | |||
| 156 | gesture.detection_count++; | ||
| 157 | type = GestureType::Touch; | ||
| 158 | |||
| 159 | // New touch after cancel is not considered new | ||
| 160 | if (last_entry.type != GestureType::Cancel) { | ||
| 161 | attributes.is_new_touch.Assign(1); | ||
| 162 | enable_press_and_tap = true; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, | ||
| 167 | f32 time_difference) { | ||
| 168 | const auto& last_entry = GetLastGestureEntry(); | ||
| 169 | |||
| 170 | // Promote to pan type if touch moved | ||
| 171 | for (size_t id = 0; id < MAX_POINTS; id++) { | ||
| 172 | if (gesture.points[id] != last_gesture.points[id]) { | ||
| 173 | type = GestureType::Pan; | ||
| 174 | break; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | // Number of fingers changed cancel the last event and clear data | ||
| 179 | if (gesture.active_points != last_gesture.active_points) { | ||
| 180 | type = GestureType::Cancel; | ||
| 181 | enable_press_and_tap = false; | ||
| 182 | gesture.active_points = 0; | ||
| 183 | gesture.mid_point = {}; | ||
| 184 | gesture.points.fill({}); | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | |||
| 188 | // Calculate extra parameters of panning | ||
| 189 | if (type == GestureType::Pan) { | ||
| 190 | UpdatePanEvent(gesture, last_gesture, type, time_difference); | ||
| 191 | return; | ||
| 192 | } | ||
| 193 | |||
| 194 | // Promote to press type | ||
| 195 | if (last_entry.type == GestureType::Touch) { | ||
| 196 | type = GestureType::Press; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 201 | GestureType& type, GestureAttribute& attributes, f32 time_difference) { | ||
| 202 | const auto& last_entry = GetLastGestureEntry(); | ||
| 203 | |||
| 204 | if (last_gesture_props.active_points != 0) { | ||
| 205 | switch (last_entry.type) { | ||
| 206 | case GestureType::Touch: | ||
| 207 | if (enable_press_and_tap) { | ||
| 208 | SetTapEvent(gesture, last_gesture_props, type, attributes); | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | type = GestureType::Cancel; | ||
| 212 | force_update = true; | ||
| 213 | break; | ||
| 214 | case GestureType::Press: | ||
| 215 | case GestureType::Tap: | ||
| 216 | case GestureType::Swipe: | ||
| 217 | case GestureType::Pinch: | ||
| 218 | case GestureType::Rotate: | ||
| 219 | type = GestureType::Complete; | ||
| 220 | force_update = true; | ||
| 221 | break; | ||
| 222 | case GestureType::Pan: | ||
| 223 | EndPanEvent(gesture, last_gesture_props, type, time_difference); | ||
| 224 | break; | ||
| 225 | default: | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) { | ||
| 231 | gesture.detection_count++; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 236 | GestureType& type, GestureAttribute& attributes) { | ||
| 237 | type = GestureType::Tap; | ||
| 238 | gesture = last_gesture_props; | ||
| 239 | force_update = true; | ||
| 240 | f32 tap_time_difference = | ||
| 241 | static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); | ||
| 242 | last_tap_timestamp = last_update_timestamp; | ||
| 243 | if (tap_time_difference < double_tap_delay) { | ||
| 244 | attributes.is_double_tap.Assign(1); | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 249 | GestureType& type, f32 time_difference) { | ||
| 250 | const auto& last_entry = GetLastGestureEntry(); | ||
| 251 | |||
| 252 | next_state.delta = gesture.mid_point - last_entry.pos; | ||
| 253 | next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; | ||
| 254 | next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; | ||
| 255 | last_pan_time_difference = time_difference; | ||
| 256 | |||
| 257 | // Promote to pinch type | ||
| 258 | if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > | ||
| 259 | pinch_threshold) { | ||
| 260 | type = GestureType::Pinch; | ||
| 261 | next_state.scale = gesture.average_distance / last_gesture_props.average_distance; | ||
| 262 | } | ||
| 263 | |||
| 264 | const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / | ||
| 265 | (1 + (gesture.angle * last_gesture_props.angle))); | ||
| 266 | // Promote to rotate type | ||
| 267 | if (std::abs(angle_between_two_lines) > angle_threshold) { | ||
| 268 | type = GestureType::Rotate; | ||
| 269 | next_state.scale = 0; | ||
| 270 | next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 275 | GestureType& type, f32 time_difference) { | ||
| 276 | const auto& last_entry = GetLastGestureEntry(); | ||
| 277 | next_state.vel_x = | ||
| 278 | static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); | ||
| 279 | next_state.vel_y = | ||
| 280 | static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); | ||
| 281 | const f32 curr_vel = | ||
| 282 | std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); | ||
| 283 | |||
| 284 | // Set swipe event with parameters | ||
| 285 | if (curr_vel > swipe_threshold) { | ||
| 286 | SetSwipeEvent(gesture, last_gesture_props, type); | ||
| 287 | return; | ||
| 288 | } | ||
| 289 | |||
| 290 | // End panning without swipe | ||
| 291 | type = GestureType::Complete; | ||
| 292 | next_state.vel_x = 0; | ||
| 293 | next_state.vel_y = 0; | ||
| 294 | force_update = true; | ||
| 295 | } | ||
| 296 | |||
| 297 | void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 298 | GestureType& type) { | ||
| 299 | const auto& last_entry = GetLastGestureEntry(); | ||
| 300 | |||
| 301 | type = GestureType::Swipe; | ||
| 302 | gesture = last_gesture_props; | ||
| 303 | force_update = true; | ||
| 304 | next_state.delta = last_entry.delta; | ||
| 305 | |||
| 306 | if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { | ||
| 307 | if (next_state.delta.x > 0) { | ||
| 308 | next_state.direction = GestureDirection::Right; | ||
| 309 | return; | ||
| 310 | } | ||
| 311 | next_state.direction = GestureDirection::Left; | ||
| 312 | return; | ||
| 313 | } | ||
| 314 | if (next_state.delta.y > 0) { | ||
| 315 | next_state.direction = GestureDirection::Down; | ||
| 316 | return; | ||
| 317 | } | ||
| 318 | next_state.direction = GestureDirection::Up; | ||
| 319 | } | ||
| 320 | |||
| 321 | const GestureState& Gesture::GetLastGestureEntry() const { | ||
| 322 | return shared_memory->gesture_lifo.ReadCurrentEntry().state; | ||
| 323 | } | ||
| 324 | |||
| 325 | GestureProperties Gesture::GetGestureProperties() { | ||
| 326 | GestureProperties gesture; | ||
| 327 | std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; | ||
| 328 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | ||
| 329 | [](const auto& finger) { return finger.pressed; }); | ||
| 330 | gesture.active_points = | ||
| 331 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | ||
| 332 | |||
| 333 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 334 | const auto& [active_x, active_y] = active_fingers[id].position; | ||
| 335 | gesture.points[id] = { | ||
| 336 | .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), | ||
| 337 | .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), | ||
| 338 | }; | ||
| 339 | |||
| 340 | // Hack: There is no touch in docked but games still allow it | ||
| 341 | if (Settings::IsDockedMode()) { | ||
| 342 | gesture.points[id] = { | ||
| 343 | .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), | ||
| 344 | .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), | ||
| 345 | }; | ||
| 346 | } | ||
| 347 | |||
| 348 | gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); | ||
| 349 | gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); | ||
| 350 | } | ||
| 351 | |||
| 352 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 353 | const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + | ||
| 354 | Square(gesture.mid_point.y - gesture.points[id].y)); | ||
| 355 | gesture.average_distance += distance / static_cast<f32>(gesture.active_points); | ||
| 356 | } | ||
| 357 | |||
| 358 | gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), | ||
| 359 | static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); | ||
| 360 | |||
| 361 | gesture.detection_count = last_gesture.detection_count; | ||
| 362 | |||
| 363 | return gesture; | ||
| 364 | } | ||
| 365 | |||
| 366 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/touch_screen/gesture.h b/src/hid_core/resources/touch_screen/gesture.h new file mode 100644 index 000000000..32e9a8690 --- /dev/null +++ b/src/hid_core/resources/touch_screen/gesture.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "hid_core/resources/controller_base.h" | ||
| 10 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 11 | |||
| 12 | namespace Core::HID { | ||
| 13 | class EmulatedConsole; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Service::HID { | ||
| 17 | struct GestureSharedMemoryFormat; | ||
| 18 | |||
| 19 | class Gesture final : public ControllerBase { | ||
| 20 | public: | ||
| 21 | explicit Gesture(Core::HID::HIDCore& hid_core_); | ||
| 22 | ~Gesture() override; | ||
| 23 | |||
| 24 | // Called when the controller is initialized | ||
| 25 | void OnInit() override; | ||
| 26 | |||
| 27 | // When the controller is released | ||
| 28 | void OnRelease() override; | ||
| 29 | |||
| 30 | // When the controller is requesting an update for the shared memory | ||
| 31 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 32 | |||
| 33 | private: | ||
| 34 | // Reads input from all available input engines | ||
| 35 | void ReadTouchInput(); | ||
| 36 | |||
| 37 | // Returns true if gesture state needs to be updated | ||
| 38 | bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); | ||
| 39 | |||
| 40 | // Updates the shared memory to the next state | ||
| 41 | void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); | ||
| 42 | |||
| 43 | // Initializes new gesture | ||
| 44 | void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); | ||
| 45 | |||
| 46 | // Updates existing gesture state | ||
| 47 | void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference); | ||
| 48 | |||
| 49 | // Terminates exiting gesture | ||
| 50 | void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 51 | GestureType& type, GestureAttribute& attributes, f32 time_difference); | ||
| 52 | |||
| 53 | // Set current event to a tap event | ||
| 54 | void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 55 | GestureType& type, GestureAttribute& attributes); | ||
| 56 | |||
| 57 | // Calculates and set the extra parameters related to a pan event | ||
| 58 | void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 59 | GestureType& type, f32 time_difference); | ||
| 60 | |||
| 61 | // Terminates the pan event | ||
| 62 | void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 63 | GestureType& type, f32 time_difference); | ||
| 64 | |||
| 65 | // Set current event to a swipe event | ||
| 66 | void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 67 | GestureType& type); | ||
| 68 | |||
| 69 | // Retrieves the last gesture entry, as indicated by shared memory indices. | ||
| 70 | [[nodiscard]] const GestureState& GetLastGestureEntry() const; | ||
| 71 | |||
| 72 | // Returns the average distance, angle and middle point of the active fingers | ||
| 73 | GestureProperties GetGestureProperties(); | ||
| 74 | |||
| 75 | GestureState next_state{}; | ||
| 76 | GestureSharedMemoryFormat* shared_memory; | ||
| 77 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 78 | |||
| 79 | std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; | ||
| 80 | GestureProperties last_gesture{}; | ||
| 81 | s64 last_update_timestamp{}; | ||
| 82 | s64 last_tap_timestamp{}; | ||
| 83 | f32 last_pan_time_difference{}; | ||
| 84 | bool force_update{false}; | ||
| 85 | bool enable_press_and_tap{false}; | ||
| 86 | }; | ||
| 87 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/touch_screen/gesture_types.h b/src/hid_core/resources/touch_screen/gesture_types.h new file mode 100644 index 000000000..b4f034cd3 --- /dev/null +++ b/src/hid_core/resources/touch_screen/gesture_types.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/point.h" | ||
| 10 | |||
| 11 | namespace Service::HID { | ||
| 12 | static constexpr size_t MAX_FINGERS = 16; | ||
| 13 | static constexpr size_t MAX_POINTS = 4; | ||
| 14 | |||
| 15 | // This is nn::hid::GestureType | ||
| 16 | enum class GestureType : u32 { | ||
| 17 | Idle, // Nothing touching the screen | ||
| 18 | Complete, // Set at the end of a touch event | ||
| 19 | Cancel, // Set when the number of fingers change | ||
| 20 | Touch, // A finger just touched the screen | ||
| 21 | Press, // Set if last type is touch and the finger hasn't moved | ||
| 22 | Tap, // Fast press then release | ||
| 23 | Pan, // All points moving together across the screen | ||
| 24 | Swipe, // Fast press movement and release of a single point | ||
| 25 | Pinch, // All points moving away/closer to the midpoint | ||
| 26 | Rotate, // All points rotating from the midpoint | ||
| 27 | }; | ||
| 28 | |||
| 29 | // This is nn::hid::GestureDirection | ||
| 30 | enum class GestureDirection : u32 { | ||
| 31 | None, | ||
| 32 | Left, | ||
| 33 | Up, | ||
| 34 | Right, | ||
| 35 | Down, | ||
| 36 | }; | ||
| 37 | |||
| 38 | // This is nn::hid::GestureAttribute | ||
| 39 | struct GestureAttribute { | ||
| 40 | union { | ||
| 41 | u32 raw{}; | ||
| 42 | |||
| 43 | BitField<4, 1, u32> is_new_touch; | ||
| 44 | BitField<8, 1, u32> is_double_tap; | ||
| 45 | }; | ||
| 46 | }; | ||
| 47 | static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); | ||
| 48 | |||
| 49 | // This is nn::hid::GestureState | ||
| 50 | struct GestureState { | ||
| 51 | s64 sampling_number{}; | ||
| 52 | s64 detection_count{}; | ||
| 53 | GestureType type{GestureType::Idle}; | ||
| 54 | GestureDirection direction{GestureDirection::None}; | ||
| 55 | Common::Point<s32> pos{}; | ||
| 56 | Common::Point<s32> delta{}; | ||
| 57 | f32 vel_x{}; | ||
| 58 | f32 vel_y{}; | ||
| 59 | GestureAttribute attributes{}; | ||
| 60 | f32 scale{}; | ||
| 61 | f32 rotation_angle{}; | ||
| 62 | s32 point_count{}; | ||
| 63 | std::array<Common::Point<s32>, 4> points{}; | ||
| 64 | }; | ||
| 65 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); | ||
| 66 | |||
| 67 | struct GestureProperties { | ||
| 68 | std::array<Common::Point<s32>, MAX_POINTS> points{}; | ||
| 69 | std::size_t active_points{}; | ||
| 70 | Common::Point<s32> mid_point{}; | ||
| 71 | s64 detection_count{}; | ||
| 72 | u64 delta_time{}; | ||
| 73 | f32 average_distance{}; | ||
| 74 | f32 angle{}; | ||
| 75 | }; | ||
| 76 | |||
| 77 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/touch_screen/touch_screen.cpp b/src/hid_core/resources/touch_screen/touch_screen.cpp new file mode 100644 index 000000000..48d956c51 --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_screen.cpp | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/settings.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 8 | #include "core/frontend/emu_window.h" | ||
| 9 | #include "hid_core/frontend/emulated_console.h" | ||
| 10 | #include "hid_core/hid_core.h" | ||
| 11 | #include "hid_core/resources/applet_resource.h" | ||
| 12 | #include "hid_core/resources/shared_memory_format.h" | ||
| 13 | #include "hid_core/resources/touch_screen/touch_screen.h" | ||
| 14 | |||
| 15 | namespace Service::HID { | ||
| 16 | |||
| 17 | TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_) | ||
| 18 | : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width), | ||
| 19 | touchscreen_height(Layout::ScreenUndocked::Height) { | ||
| 20 | console = hid_core.GetEmulatedConsole(); | ||
| 21 | } | ||
| 22 | |||
| 23 | TouchScreen::~TouchScreen() = default; | ||
| 24 | |||
| 25 | void TouchScreen::OnInit() {} | ||
| 26 | |||
| 27 | void TouchScreen::OnRelease() {} | ||
| 28 | |||
| 29 | void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 30 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 31 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 32 | |||
| 33 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | |||
| 37 | TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen; | ||
| 38 | shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); | ||
| 39 | |||
| 40 | if (!IsControllerActivated()) { | ||
| 41 | shared_memory.touch_screen_lifo.buffer_count = 0; | ||
| 42 | shared_memory.touch_screen_lifo.buffer_tail = 0; | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | |||
| 46 | const auto touch_status = console->GetTouch(); | ||
| 47 | for (std::size_t id = 0; id < MAX_FINGERS; id++) { | ||
| 48 | const auto& current_touch = touch_status[id]; | ||
| 49 | auto& finger = fingers[id]; | ||
| 50 | finger.id = current_touch.id; | ||
| 51 | |||
| 52 | if (finger.attribute.start_touch) { | ||
| 53 | finger.attribute.raw = 0; | ||
| 54 | continue; | ||
| 55 | } | ||
| 56 | |||
| 57 | if (finger.attribute.end_touch) { | ||
| 58 | finger.attribute.raw = 0; | ||
| 59 | finger.pressed = false; | ||
| 60 | continue; | ||
| 61 | } | ||
| 62 | |||
| 63 | if (!finger.pressed && current_touch.pressed) { | ||
| 64 | // Ignore all touch fingers if disabled | ||
| 65 | if (!Settings::values.touchscreen.enabled) { | ||
| 66 | continue; | ||
| 67 | } | ||
| 68 | |||
| 69 | finger.attribute.start_touch.Assign(1); | ||
| 70 | finger.pressed = true; | ||
| 71 | finger.position = current_touch.position; | ||
| 72 | continue; | ||
| 73 | } | ||
| 74 | |||
| 75 | if (finger.pressed && !current_touch.pressed) { | ||
| 76 | finger.attribute.raw = 0; | ||
| 77 | finger.attribute.end_touch.Assign(1); | ||
| 78 | continue; | ||
| 79 | } | ||
| 80 | |||
| 81 | // Only update position if touch is not on a special frame | ||
| 82 | finger.position = current_touch.position; | ||
| 83 | } | ||
| 84 | |||
| 85 | std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; | ||
| 86 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | ||
| 87 | [](const auto& finger) { return finger.pressed; }); | ||
| 88 | const auto active_fingers_count = | ||
| 89 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | ||
| 90 | |||
| 91 | const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count()); | ||
| 92 | const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state; | ||
| 93 | |||
| 94 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 95 | next_state.entry_count = static_cast<s32>(active_fingers_count); | ||
| 96 | |||
| 97 | for (std::size_t id = 0; id < MAX_FINGERS; ++id) { | ||
| 98 | auto& touch_entry = next_state.states[id]; | ||
| 99 | if (id < active_fingers_count) { | ||
| 100 | const auto& [active_x, active_y] = active_fingers[id].position; | ||
| 101 | touch_entry.position = { | ||
| 102 | .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)), | ||
| 103 | .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)), | ||
| 104 | }; | ||
| 105 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | ||
| 106 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | ||
| 107 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; | ||
| 108 | touch_entry.delta_time = timestamp - active_fingers[id].last_touch; | ||
| 109 | fingers[active_fingers[id].id].last_touch = timestamp; | ||
| 110 | touch_entry.finger = active_fingers[id].id; | ||
| 111 | touch_entry.attribute.raw = active_fingers[id].attribute.raw; | ||
| 112 | } else { | ||
| 113 | // Clear touch entry | ||
| 114 | touch_entry.attribute.raw = 0; | ||
| 115 | touch_entry.position = {}; | ||
| 116 | touch_entry.diameter_x = 0; | ||
| 117 | touch_entry.diameter_y = 0; | ||
| 118 | touch_entry.rotation_angle = 0; | ||
| 119 | touch_entry.delta_time = 0; | ||
| 120 | touch_entry.finger = 0; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | shared_memory.touch_screen_lifo.WriteNextEntry(next_state); | ||
| 125 | } | ||
| 126 | |||
| 127 | void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { | ||
| 128 | touchscreen_width = width; | ||
| 129 | touchscreen_height = height; | ||
| 130 | } | ||
| 131 | |||
| 132 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/touch_screen/touch_screen.h b/src/hid_core/resources/touch_screen/touch_screen.h new file mode 100644 index 000000000..4b3824742 --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_screen.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "hid_core/hid_types.h" | ||
| 9 | #include "hid_core/resources/controller_base.h" | ||
| 10 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 11 | |||
| 12 | namespace Core::HID { | ||
| 13 | class EmulatedConsole; | ||
| 14 | } // namespace Core::HID | ||
| 15 | |||
| 16 | namespace Service::HID { | ||
| 17 | struct TouchScreenSharedMemoryFormat; | ||
| 18 | |||
| 19 | class TouchScreen final : public ControllerBase { | ||
| 20 | public: | ||
| 21 | explicit TouchScreen(Core::HID::HIDCore& hid_core_); | ||
| 22 | ~TouchScreen() override; | ||
| 23 | |||
| 24 | // Called when the controller is initialized | ||
| 25 | void OnInit() override; | ||
| 26 | |||
| 27 | // When the controller is released | ||
| 28 | void OnRelease() override; | ||
| 29 | |||
| 30 | // When the controller is requesting an update for the shared memory | ||
| 31 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 32 | |||
| 33 | void SetTouchscreenDimensions(u32 width, u32 height); | ||
| 34 | |||
| 35 | private: | ||
| 36 | TouchScreenState next_state{}; | ||
| 37 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 38 | |||
| 39 | std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; | ||
| 40 | u32 touchscreen_width; | ||
| 41 | u32 touchscreen_height; | ||
| 42 | }; | ||
| 43 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/touch_screen/touch_types.h b/src/hid_core/resources/touch_screen/touch_types.h new file mode 100644 index 000000000..97ee847da --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_types.h | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include <array> | ||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/point.h" | ||
| 13 | #include "hid_core/hid_types.h" | ||
| 14 | |||
| 15 | namespace Service::HID { | ||
| 16 | static constexpr std::size_t MAX_FINGERS = 16; | ||
| 17 | static constexpr size_t MAX_POINTS = 4; | ||
| 18 | |||
| 19 | // This is nn::hid::GestureType | ||
| 20 | enum class GestureType : u32 { | ||
| 21 | Idle, // Nothing touching the screen | ||
| 22 | Complete, // Set at the end of a touch event | ||
| 23 | Cancel, // Set when the number of fingers change | ||
| 24 | Touch, // A finger just touched the screen | ||
| 25 | Press, // Set if last type is touch and the finger hasn't moved | ||
| 26 | Tap, // Fast press then release | ||
| 27 | Pan, // All points moving together across the screen | ||
| 28 | Swipe, // Fast press movement and release of a single point | ||
| 29 | Pinch, // All points moving away/closer to the midpoint | ||
| 30 | Rotate, // All points rotating from the midpoint | ||
| 31 | }; | ||
| 32 | |||
| 33 | // This is nn::hid::GestureDirection | ||
| 34 | enum class GestureDirection : u32 { | ||
| 35 | None, | ||
| 36 | Left, | ||
| 37 | Up, | ||
| 38 | Right, | ||
| 39 | Down, | ||
| 40 | }; | ||
| 41 | |||
| 42 | // This is nn::hid::GestureAttribute | ||
| 43 | struct GestureAttribute { | ||
| 44 | union { | ||
| 45 | u32 raw{}; | ||
| 46 | |||
| 47 | BitField<4, 1, u32> is_new_touch; | ||
| 48 | BitField<8, 1, u32> is_double_tap; | ||
| 49 | }; | ||
| 50 | }; | ||
| 51 | static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); | ||
| 52 | |||
| 53 | // This is nn::hid::GestureState | ||
| 54 | struct GestureState { | ||
| 55 | s64 sampling_number{}; | ||
| 56 | s64 detection_count{}; | ||
| 57 | GestureType type{GestureType::Idle}; | ||
| 58 | GestureDirection direction{GestureDirection::None}; | ||
| 59 | Common::Point<s32> pos{}; | ||
| 60 | Common::Point<s32> delta{}; | ||
| 61 | f32 vel_x{}; | ||
| 62 | f32 vel_y{}; | ||
| 63 | GestureAttribute attributes{}; | ||
| 64 | f32 scale{}; | ||
| 65 | f32 rotation_angle{}; | ||
| 66 | s32 point_count{}; | ||
| 67 | std::array<Common::Point<s32>, 4> points{}; | ||
| 68 | }; | ||
| 69 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); | ||
| 70 | |||
| 71 | struct GestureProperties { | ||
| 72 | std::array<Common::Point<s32>, MAX_POINTS> points{}; | ||
| 73 | std::size_t active_points{}; | ||
| 74 | Common::Point<s32> mid_point{}; | ||
| 75 | s64 detection_count{}; | ||
| 76 | u64 delta_time{}; | ||
| 77 | f32 average_distance{}; | ||
| 78 | f32 angle{}; | ||
| 79 | }; | ||
| 80 | |||
| 81 | // This is nn::hid::TouchScreenState | ||
| 82 | struct TouchScreenState { | ||
| 83 | s64 sampling_number{}; | ||
| 84 | s32 entry_count{}; | ||
| 85 | INSERT_PADDING_BYTES(4); // Reserved | ||
| 86 | std::array<Core::HID::TouchState, MAX_FINGERS> states{}; | ||
| 87 | }; | ||
| 88 | static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); | ||
| 89 | |||
| 90 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/unique_pad/unique_pad.cpp b/src/hid_core/resources/unique_pad/unique_pad.cpp new file mode 100644 index 000000000..892bbe3c9 --- /dev/null +++ b/src/hid_core/resources/unique_pad/unique_pad.cpp | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "hid_core/resources/applet_resource.h" | ||
| 6 | #include "hid_core/resources/shared_memory_format.h" | ||
| 7 | #include "hid_core/resources/unique_pad/unique_pad.h" | ||
| 8 | |||
| 9 | namespace Service::HID { | ||
| 10 | |||
| 11 | UniquePad::UniquePad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} | ||
| 12 | |||
| 13 | UniquePad::~UniquePad() = default; | ||
| 14 | |||
| 15 | void UniquePad::OnInit() {} | ||
| 16 | |||
| 17 | void UniquePad::OnRelease() {} | ||
| 18 | |||
| 19 | void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 20 | if (!smart_update) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | |||
| 24 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 25 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 26 | |||
| 27 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | |||
| 31 | auto& header = data->shared_memory_format->capture_button.header; | ||
| 32 | header.timestamp = core_timing.GetGlobalTimeNs().count(); | ||
| 33 | header.total_entry_count = 17; | ||
| 34 | header.entry_count = 0; | ||
| 35 | header.last_entry_index = 0; | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace Service::HID | ||
diff --git a/src/hid_core/resources/unique_pad/unique_pad.h b/src/hid_core/resources/unique_pad/unique_pad.h new file mode 100644 index 000000000..674ad1691 --- /dev/null +++ b/src/hid_core/resources/unique_pad/unique_pad.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "hid_core/resources/controller_base.h" | ||
| 7 | |||
| 8 | namespace Service::HID { | ||
| 9 | |||
| 10 | class UniquePad final : public ControllerBase { | ||
| 11 | public: | ||
| 12 | explicit UniquePad(Core::HID::HIDCore& hid_core_); | ||
| 13 | ~UniquePad() override; | ||
| 14 | |||
| 15 | // Called when the controller is initialized | ||
| 16 | void OnInit() override; | ||
| 17 | |||
| 18 | // When the controller is released | ||
| 19 | void OnRelease() override; | ||
| 20 | |||
| 21 | // When the controller is requesting an update for the shared memory | ||
| 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 23 | |||
| 24 | private: | ||
| 25 | bool smart_update{}; | ||
| 26 | }; | ||
| 27 | } // namespace Service::HID | ||