summaryrefslogtreecommitdiff
path: root/src/hid_core/resources
diff options
context:
space:
mode:
authorGravatar Narr the Reg2024-01-04 20:37:43 -0600
committerGravatar Narr the Reg2024-01-05 11:41:15 -0600
commitee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch)
tree3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/hid_core/resources
parentMerge pull request #12437 from ameerj/gl-amd-fixes (diff)
downloadyuzu-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')
-rw-r--r--src/hid_core/resources/applet_resource.cpp329
-rw-r--r--src/hid_core/resources/applet_resource.h123
-rw-r--r--src/hid_core/resources/controller_base.cpp41
-rw-r--r--src/hid_core/resources/controller_base.h55
-rw-r--r--src/hid_core/resources/debug_pad/debug_pad.cpp59
-rw-r--r--src/hid_core/resources/debug_pad/debug_pad.h37
-rw-r--r--src/hid_core/resources/debug_pad/debug_pad_types.h31
-rw-r--r--src/hid_core/resources/digitizer/digitizer.cpp39
-rw-r--r--src/hid_core/resources/digitizer/digitizer.h27
-rw-r--r--src/hid_core/resources/hid_firmware_settings.cpp99
-rw-r--r--src/hid_core/resources/hid_firmware_settings.h54
-rw-r--r--src/hid_core/resources/irs_ring_lifo.h47
-rw-r--r--src/hid_core/resources/keyboard/keyboard.cpp56
-rw-r--r--src/hid_core/resources/keyboard/keyboard.h33
-rw-r--r--src/hid_core/resources/keyboard/keyboard_types.h20
-rw-r--r--src/hid_core/resources/mouse/debug_mouse.cpp64
-rw-r--r--src/hid_core/resources/mouse/debug_mouse.h34
-rw-r--r--src/hid_core/resources/mouse/mouse.cpp64
-rw-r--r--src/hid_core/resources/mouse/mouse.h34
-rw-r--r--src/hid_core/resources/mouse/mouse_types.h8
-rw-r--r--src/hid_core/resources/npad/npad.cpp1342
-rw-r--r--src/hid_core/resources/npad/npad.h214
-rw-r--r--src/hid_core/resources/npad/npad_data.cpp228
-rw-r--r--src/hid_core/resources/npad/npad_data.h88
-rw-r--r--src/hid_core/resources/npad/npad_resource.cpp685
-rw-r--r--src/hid_core/resources/npad/npad_resource.h132
-rw-r--r--src/hid_core/resources/npad/npad_types.h255
-rw-r--r--src/hid_core/resources/palma/palma.cpp225
-rw-r--r--src/hid_core/resources/palma/palma.h163
-rw-r--r--src/hid_core/resources/ring_lifo.h53
-rw-r--r--src/hid_core/resources/shared_memory_format.h240
-rw-r--r--src/hid_core/resources/shared_memory_holder.cpp54
-rw-r--r--src/hid_core/resources/shared_memory_holder.h44
-rw-r--r--src/hid_core/resources/six_axis/console_six_axis.cpp45
-rw-r--r--src/hid_core/resources/six_axis/console_six_axis.h30
-rw-r--r--src/hid_core/resources/six_axis/seven_six_axis.cpp66
-rw-r--r--src/hid_core/resources/six_axis/seven_six_axis.h65
-rw-r--r--src/hid_core/resources/six_axis/six_axis.cpp421
-rw-r--r--src/hid_core/resources/six_axis/six_axis.h111
-rw-r--r--src/hid_core/resources/system_buttons/capture_button.cpp39
-rw-r--r--src/hid_core/resources/system_buttons/capture_button.h27
-rw-r--r--src/hid_core/resources/system_buttons/home_button.cpp39
-rw-r--r--src/hid_core/resources/system_buttons/home_button.h27
-rw-r--r--src/hid_core/resources/system_buttons/sleep_button.cpp39
-rw-r--r--src/hid_core/resources/system_buttons/sleep_button.h27
-rw-r--r--src/hid_core/resources/touch_screen/gesture.cpp366
-rw-r--r--src/hid_core/resources/touch_screen/gesture.h87
-rw-r--r--src/hid_core/resources/touch_screen/gesture_types.h77
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen.cpp132
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen.h43
-rw-r--r--src/hid_core/resources/touch_screen/touch_types.h90
-rw-r--r--src/hid_core/resources/unique_pad/unique_pad.cpp38
-rw-r--r--src/hid_core/resources/unique_pad/unique_pad.h27
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
10namespace Service::HID {
11
12AppletResource::AppletResource(Core::System& system_) : system{system_} {}
13
14AppletResource::~AppletResource() = default;
15
16Result 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
51Result 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
106void 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
129void 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
142u64 AppletResource::GetActiveAruid() {
143 return active_aruid;
144}
145
146Result 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
156Result 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
167AruidData* 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
175AruidData* AppletResource::GetAruidDataByIndex(std::size_t aruid_index) {
176 return &data[aruid_index];
177}
178
179bool AppletResource::IsVibrationAruidActive(u64 aruid) const {
180 return aruid == 0 || aruid == active_vibration_aruid;
181}
182
183u64 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
193Result AppletResource::DestroySevenSixAxisTransferMemory() {
194 // TODO
195 return ResultSuccess;
196}
197
198void 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
208void 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
217void 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
226void 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
235void 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
244void 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
253Result 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
317Result 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
14namespace Core {
15class System;
16}
17
18namespace Kernel {
19class KSharedMemory;
20}
21
22namespace Service::HID {
23struct SharedMemoryFormat;
24class AppletResource;
25class NPadResource;
26
27static constexpr std::size_t AruidIndexMax = 0x20;
28static constexpr u64 SystemAruid = 0;
29
30enum class RegistrationStatus : u32 {
31 None,
32 Initialized,
33 PendingDelete,
34};
35
36struct 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
51struct AruidRegisterList {
52 std::array<RegistrationStatus, AruidIndexMax> flag{};
53 std::array<u64, AruidIndexMax> aruid{};
54};
55static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
56
57struct AruidData {
58 DataStatusFlag flag{};
59 u64 aruid{};
60 SharedMemoryFormat* shared_memory_format{nullptr};
61};
62
63struct 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};
69static_assert(sizeof(HandheldConfig) == 0x4, "HandheldConfig is an invalid size");
70
71struct 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
79class AppletResource {
80public:
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
113private:
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
6namespace Service::HID {
7
8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
9ControllerBase::~ControllerBase() = default;
10
11Result ControllerBase::Activate() {
12 if (is_activated) {
13 return ResultSuccess;
14 }
15 is_activated = true;
16 OnInit();
17 return ResultSuccess;
18}
19
20Result ControllerBase::Activate(u64 aruid) {
21 return Activate();
22}
23
24void ControllerBase::DeactivateController() {
25 if (is_activated) {
26 OnRelease();
27 }
28 is_activated = false;
29}
30
31bool ControllerBase::IsControllerActivated() const {
32 return is_activated;
33}
34
35void 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
12namespace Core::Timing {
13class CoreTiming;
14}
15
16namespace Core::HID {
17class HIDCore;
18} // namespace Core::HID
19
20namespace Service::HID {
21class ControllerBase {
22public:
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
48protected:
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
13namespace Service::HID {
14
15DebugPad::DebugPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
16 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
17}
18
19DebugPad::~DebugPad() = default;
20
21void DebugPad::OnInit() {}
22
23void DebugPad::OnRelease() {}
24
25void 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
9namespace Core::HID {
10class HIDCore;
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Core::Timing {
15class CoreTiming;
16}
17
18namespace Service::HID {
19class DebugPad final : public ControllerBase {
20public:
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
33private:
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
10namespace Service::HID {
11
12// This is nn::hid::DebugPadAttribute
13struct DebugPadAttribute {
14 union {
15 u32 raw{};
16 BitField<0, 1, u32> connected;
17 };
18};
19static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
20
21// This is nn::hid::DebugPadState
22struct 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};
29static_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
9namespace Service::HID {
10
11Digitizer::Digitizer(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13Digitizer::~Digitizer() = default;
14
15void Digitizer::OnInit() {}
16
17void Digitizer::OnRelease() {}
18
19void 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
8namespace Service::HID {
9
10class Digitizer final : public ControllerBase {
11public:
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
24private:
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
6namespace Service::HID {
7
8HidFirmwareSettings::HidFirmwareSettings() {
9 LoadSettings(true);
10}
11
12void HidFirmwareSettings::Reload() {
13 LoadSettings(true);
14}
15
16void 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
39bool HidFirmwareSettings::IsDebugPadEnabled() {
40 LoadSettings(false);
41 return is_debug_pad_enabled;
42}
43
44bool HidFirmwareSettings::IsDeviceManaged() {
45 LoadSettings(false);
46 return is_device_managed;
47}
48
49bool HidFirmwareSettings::IsEmulateFutureDevice() {
50 LoadSettings(false);
51 return is_future_devices_emulated;
52}
53
54bool HidFirmwareSettings::IsTouchI2cManaged() {
55 LoadSettings(false);
56 return is_touch_i2c_managed;
57}
58
59bool HidFirmwareSettings::IsHandheldForced() {
60 LoadSettings(false);
61 return is_handheld_forced;
62}
63
64bool HidFirmwareSettings::IsRailEnabled() {
65 LoadSettings(false);
66 return is_rail_enabled;
67}
68
69bool HidFirmwareSettings::IsHardwareErrorEmulated() {
70 LoadSettings(false);
71 return is_mcu_hardware_error_emulated;
72}
73
74bool HidFirmwareSettings::IsBleDisabled() {
75 LoadSettings(false);
76 return is_ble_disabled;
77}
78
79bool HidFirmwareSettings::IsDscaleDisabled() {
80 LoadSettings(false);
81 return is_dscale_disabled;
82}
83
84bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
85 LoadSettings(false);
86 return is_touch_firmware_auto_update_disabled;
87}
88
89HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
90 LoadSettings(false);
91 return is_firmware_update_failure;
92}
93
94HidFirmwareSettings::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
8namespace Service::HID {
9
10/// Loads firmware config from nn::settings::fwdbg
11class HidFirmwareSettings {
12public:
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
35private:
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
10namespace Service::IRS {
11
12template <typename State, std::size_t max_buffer_size>
13struct 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
12namespace Service::HID {
13
14Keyboard::Keyboard(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
15 emulated_devices = hid_core.GetEmulatedDevices();
16}
17
18Keyboard::~Keyboard() = default;
19
20void Keyboard::OnInit() {}
21
22void Keyboard::OnRelease() {}
23
24void 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
9namespace Core::HID {
10class HIDCore;
11class EmulatedDevices;
12} // namespace Core::HID
13
14namespace Service::HID {
15class Keyboard final : public ControllerBase {
16public:
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
29private:
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
9namespace Service::HID {
10
11// This is nn::hid::detail::KeyboardState
12struct KeyboardState {
13 s64 sampling_number{};
14 Core::HID::KeyboardModifier modifier{};
15 Core::HID::KeyboardAttribute attribute{};
16 Core::HID::KeyboardKey key{};
17};
18static_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
12namespace Service::HID {
13
14DebugMouse::DebugMouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
15 emulated_devices = hid_core.GetEmulatedDevices();
16}
17
18DebugMouse::~DebugMouse() = default;
19
20void DebugMouse::OnInit() {}
21void DebugMouse::OnRelease() {}
22
23void 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
9namespace Core::HID {
10class HIDCore;
11class EmulatedDevices;
12} // namespace Core::HID
13
14namespace Service::HID {
15class DebugMouse final : public ControllerBase {
16public:
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
29private:
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
12namespace Service::HID {
13
14Mouse::Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
15 emulated_devices = hid_core.GetEmulatedDevices();
16}
17
18Mouse::~Mouse() = default;
19
20void Mouse::OnInit() {}
21void Mouse::OnRelease() {}
22
23void 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
9namespace Core::HID {
10class HIDCore;
11class EmulatedDevices;
12} // namespace Core::HID
13
14namespace Service::HID {
15class Mouse final : public ControllerBase {
16public:
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
29private:
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
8namespace 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
26namespace Service::HID {
27
28NPad::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
48NPad::~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
57Result 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
72Result 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
109Result NPad::ActivateNpadResource() {
110 return npad_resource.Activate();
111}
112
113Result NPad::ActivateNpadResource(u64 aruid) {
114 return npad_resource.Activate(aruid);
115}
116
117void 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
164void 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
364void 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
385void 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
454void 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
596Result 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
606Result 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
619Result 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
633Result 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
649Result NPad::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) {
650 std::scoped_lock lock{mutex};
651 return npad_resource.SetNpadJoyHoldType(aruid, hold_type);
652}
653
654Result 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
659Result 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
668Result NPad::GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const {
669 std::scoped_lock lock{mutex};
670 return npad_resource.GetNpadHandheldActivationMode(out_mode, aruid);
671}
672
673bool 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
743bool 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
789void 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
836void 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
853Core::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
864void 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
876void 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
888void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
889 permit_vibration_session_enabled = permit_vibration_session;
890}
891
892bool 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
903Result 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
909void NPad::AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
910 Core::HID::NpadIdType npad_id) {
911 UpdateControllerAt(aruid, controller, npad_id, true);
912}
913
914void 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
926Result 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
975Result 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
989Result 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
1003Result 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
1066Result 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
1076Result 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
1086Result 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
1118Result 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
1129Result 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
1135Result 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
1141void NPad::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) {
1142 std::scoped_lock lock{mutex};
1143 npad_resource.SetNpadAnalogStickUseCenterClamp(aruid, is_enabled);
1144}
1145
1146void 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
1158void 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
1166void 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
1177void 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
1186Core::HID::NpadButton NPad::GetAndResetPressState() {
1187 return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
1188}
1189
1190Result 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
1199Result 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
1208Result 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
1217void NPad::SetRevision(u64 aruid, NpadRevision revision) {
1218 npad_resource.SetNpadRevision(aruid, revision);
1219}
1220
1221NpadRevision NPad::GetRevision(u64 aruid) {
1222 return npad_resource.GetNpadRevision(aruid);
1223}
1224
1225Result NPad::RegisterAppletResourceUserId(u64 aruid) {
1226 return npad_resource.RegisterAppletResourceUserId(aruid);
1227}
1228
1229void NPad::UnregisterAppletResourceUserId(u64 aruid) {
1230 npad_resource.UnregisterAppletResourceUserId(aruid);
1231}
1232
1233void 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
1240NPad::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
1246const 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
1252NPad::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
1258const 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
1264NPad::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
1275const 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
1286Core::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
1309const 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
1332AppletDetailedUiType 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
17namespace Core::HID {
18class EmulatedController;
19enum class ControllerTriggerType;
20} // namespace Core::HID
21
22namespace Kernel {
23class KEvent;
24class KReadableEvent;
25} // namespace Kernel
26
27namespace Service::KernelHelpers {
28class ServiceContext;
29} // namespace Service::KernelHelpers
30
31union Result;
32
33namespace Service::HID {
34class AppletResource;
35struct NpadInternalState;
36struct NpadSixAxisSensorLifo;
37struct NpadSharedMemoryFormat;
38
39class NPad final {
40public:
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
152private:
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
7namespace Service::HID {
8
9NPadData::NPadData() {
10 ClearNpadSystemCommonPolicy();
11}
12
13NPadData::~NPadData() = default;
14
15NpadStatus NPadData::GetNpadStatus() const {
16 return status;
17}
18
19void NPadData::SetNpadAnalogStickUseCenterClamp(bool is_enabled) {
20 status.use_center_clamp.Assign(is_enabled);
21}
22
23bool NPadData::GetNpadAnalogStickUseCenterClamp() const {
24 return status.use_center_clamp.As<bool>();
25}
26
27void NPadData::SetNpadSystemExtStateEnabled(bool is_enabled) {
28 status.system_ext_state.Assign(is_enabled);
29}
30
31bool NPadData::GetNpadSystemExtState() const {
32 return status.system_ext_state.As<bool>();
33}
34
35Result 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
48std::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
57bool 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
67void 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
97void 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
124void NPadData::SetNpadJoyHoldType(NpadJoyHoldType hold_type) {
125 npad_hold_type = hold_type;
126 status.is_hold_type_set.Assign(true);
127}
128
129NpadJoyHoldType NPadData::GetNpadJoyHoldType() const {
130 return npad_hold_type;
131}
132
133void NPadData::SetHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
134 handheld_activation_mode = activation_mode;
135}
136
137NpadHandheldActivationMode NPadData::GetHandheldActivationMode() const {
138 return handheld_activation_mode;
139}
140
141void 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
147Core::HID::NpadStyleSet NPadData::GetSupportedNpadStyleSet() const {
148 return supported_npad_style_set;
149}
150
151bool 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
181void NPadData::SetLrAssignmentMode(bool is_enabled) {
182 status.lr_assignment_mode.Assign(is_enabled);
183}
184
185bool NPadData::GetLrAssignmentMode() const {
186 return status.lr_assignment_mode.As<bool>();
187}
188
189void NPadData::SetAssigningSingleOnSlSrPress(bool is_enabled) {
190 status.assigning_single_on_sl_sr_press.Assign(is_enabled);
191}
192
193bool NPadData::GetAssigningSingleOnSlSrPress() const {
194 return status.assigning_single_on_sl_sr_press.As<bool>();
195}
196
197void NPadData::SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id) {
198 is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)] = is_enabled;
199}
200
201bool NPadData::GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const {
202 return is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)];
203}
204
205void NPadData::SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment,
206 std::size_t style_index) {
207 npad_button_assignment[style_index] = button_assignment;
208}
209
210Core::HID::NpadButton NPadData::GetCaptureButtonAssignment(std::size_t style_index) const {
211 return npad_button_assignment[style_index];
212}
213
214std::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
14namespace Service::HID {
15
16struct 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};
30static_assert(sizeof(NpadStatus) == 4, "NpadStatus is an invalid size");
31
32/// Handles Npad request from HID interfaces
33class NPadData final {
34public:
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
77private:
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
11namespace Service::HID {
12
13NPadResource::NPadResource(KernelHelpers::ServiceContext& context) : service_context{context} {}
14
15NPadResource::~NPadResource() = default;
16
17Result 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
65void 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
75void 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
91Result 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
116Result 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
128Result NPadResource::Deactivate() {
129 if (ref_counter == 0) {
130 return ResultAppletResourceNotInitialized;
131 }
132
133 UnregisterAppletResourceUserId(SystemAruid);
134 ref_counter--;
135 return ResultSuccess;
136}
137
138NPadData* NPadResource::GetActiveData() {
139 return &active_data;
140}
141
142u64 NPadResource::GetActiveDataAruid() {
143 return active_data_aruid;
144}
145
146void 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
170std::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
180Result 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
196Result 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
209Result 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
224Result 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
240Result 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
299Result 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
349NpadRevision 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
358Result 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
368Result 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
381Result 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
396Result 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
410Result 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
421Result 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
439bool 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
447Result 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
460Result 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
470Result 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
483Result 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
493Result 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
520Result 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
532Result 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
543Result 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
557Result 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
570Result 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
581Core::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
599void NPadResource::ResetButtonConfig() {
600 for (auto& selected_state : state) {
601 selected_state.button_config = {};
602 }
603}
604
605Result 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
639Result 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
654std::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
663void 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
672Result 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
18namespace Core {
19class System;
20}
21
22namespace Kernel {
23class KReadableEvent;
24}
25
26namespace Service::HID {
27struct DataStatusFlag;
28
29struct 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
36struct 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
47class NPadResource final {
48public:
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
122private:
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
11namespace Service::HID {
12static constexpr std::size_t MaxSupportedNpadIdTypes = 10;
13static constexpr std::size_t StyleIndexCount = 7;
14
15// This is nn::hid::NpadJoyHoldType
16enum class NpadJoyHoldType : u64 {
17 Vertical = 0,
18 Horizontal = 1,
19};
20
21// This is nn::hid::NpadJoyAssignmentMode
22enum class NpadJoyAssignmentMode : u32 {
23 Dual = 0,
24 Single = 1,
25};
26
27// This is nn::hid::NpadJoyDeviceType
28enum class NpadJoyDeviceType : s64 {
29 Left = 0,
30 Right = 1,
31};
32
33// This is nn::hid::NpadHandheldActivationMode
34enum class NpadHandheldActivationMode : u64 {
35 Dual = 0,
36 Single = 1,
37 None = 2,
38 MaxActivationMode = 3,
39};
40
41// This is nn::hid::system::AppletFooterUiAttributesSet
42struct AppletFooterUiAttributes {
43 INSERT_PADDING_BYTES(0x4);
44};
45
46// This is nn::hid::system::AppletFooterUiType
47enum 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
72using AppletFooterUiVariant = u8;
73
74// This is "nn::hid::system::AppletDetailedUiType".
75struct AppletDetailedUiType {
76 AppletFooterUiVariant ui_variant;
77 INSERT_PADDING_BYTES(0x2);
78 AppletFooterUiType footer;
79};
80static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
81// This is nn::hid::NpadCommunicationMode
82enum class NpadCommunicationMode : u64 {
83 Mode_5ms = 0,
84 Mode_10ms = 1,
85 Mode_15ms = 2,
86 Default = 3,
87};
88
89enum class NpadRevision : u32 {
90 Revision0 = 0,
91 Revision1 = 1,
92 Revision2 = 2,
93 Revision3 = 3,
94};
95
96// This is nn::hid::detail::ColorAttribute
97enum class ColorAttribute : u32 {
98 Ok = 0,
99 ReadError = 1,
100 NoController = 2,
101};
102static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
103
104// This is nn::hid::detail::NpadFullKeyColorState
105struct NpadFullKeyColorState {
106 ColorAttribute attribute{ColorAttribute::NoController};
107 Core::HID::NpadControllerColor fullkey{};
108};
109static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
110
111// This is nn::hid::detail::NpadJoyColorState
112struct NpadJoyColorState {
113 ColorAttribute attribute{ColorAttribute::NoController};
114 Core::HID::NpadControllerColor left{};
115 Core::HID::NpadControllerColor right{};
116};
117static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
118
119// This is nn::hid::NpadAttribute
120struct 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};
131static_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
140struct 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};
148static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
149
150// This is nn::hid::server::NpadGcTriggerState
151struct NpadGcTriggerState {
152 s64 sampling_number{};
153 s32 l_analog{};
154 s32 r_analog{};
155};
156static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
157
158// This is nn::hid::NpadSystemProperties
159struct 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};
177static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
178
179// This is nn::hid::NpadSystemButtonProperties
180struct NpadSystemButtonProperties {
181 union {
182 s32 raw{};
183 BitField<0, 1, s32> is_home_button_protection_enabled;
184 };
185};
186static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
187
188// This is nn::hid::system::DeviceType
189struct 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
215struct NfcXcdDeviceHandleStateImpl {
216 u64 handle{};
217 bool is_available{};
218 bool is_activated{};
219 INSERT_PADDING_BYTES(0x6); // Reserved
220 u64 sampling_number{};
221};
222static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
223 "NfcXcdDeviceHandleStateImpl is an invalid size");
224
225// This is nn::hid::NpadLarkType
226enum class NpadLarkType : u32 {
227 Invalid,
228 H1,
229 H2,
230 NL,
231 NR,
232};
233
234// This is nn::hid::NpadLuciaType
235enum class NpadLuciaType : u32 {
236 Invalid,
237 J,
238 E,
239 U,
240};
241
242// This is nn::hid::NpadLagonType
243enum class NpadLagonType : u32 {
244 Invalid,
245};
246
247// This is nn::hid::NpadLagerType
248enum 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
12namespace Service::HID {
13
14Palma::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
20Palma::~Palma() {
21 service_context.CloseEvent(operation_complete_event);
22};
23
24void Palma::OnInit() {}
25
26void Palma::OnRelease() {}
27
28void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
29 if (!IsControllerActivated()) {
30 return;
31 }
32}
33
34Result 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
41Result 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
49Kernel::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
57Result 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
68Result 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
79Result 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
87Result 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
98Result 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
105Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
106 if (handle.npad_id != active_handle.npad_id) {
107 return InvalidPalmaHandle;
108 }
109 return ResultSuccess;
110}
111
112void Palma::ReadPalmaApplicationSection() {}
113
114void Palma::WritePalmaApplicationSection() {}
115
116Result 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
127Result 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
138void Palma::WritePalmaActivityEntry() {}
139
140Result 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
151Result 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
163Result 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
176Result 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
188void Palma::SuspendPalmaFeature() {}
189
190Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
191 if (handle.npad_id != active_handle.npad_id) {
192 return InvalidPalmaHandle;
193 }
194 return operation.result;
195}
196void Palma::ReadPalmaPlayLog() {}
197
198void Palma::ResetPalmaPlayLog() {}
199
200void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
201 // If true controllers are able to be paired
202 is_connectable = is_all_connectable;
203}
204
205void Palma::SetIsPalmaPairedConnectable() {}
206
207Result 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
215void Palma::SetPalmaBoostMode(bool boost_mode) {}
216
217void Palma::CancelWritePalmaWaveEntry() {}
218
219void Palma::EnablePalmaBoostMode() {}
220
221void Palma::GetPalmaBluetoothAddress() {}
222
223void 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
13namespace Kernel {
14class KEvent;
15class KReadableEvent;
16} // namespace Kernel
17
18namespace Service::KernelHelpers {
19class ServiceContext;
20}
21
22namespace Core::HID {
23class EmulatedController;
24} // namespace Core::HID
25
26namespace Service::HID {
27class Palma final : public ControllerBase {
28public:
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
138private:
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
10namespace Service::HID {
11
12template <typename State>
13struct AtomicStorage {
14 s64 sampling_number;
15 State state;
16};
17
18template <typename State, std::size_t max_buffer_size>
19struct 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
17namespace Service::HID {
18static const std::size_t HidEntryCount = 17;
19
20struct CommonHeader {
21 s64 timestamp{};
22 s64 total_entry_count{};
23 s64 last_entry_index{};
24 s64 entry_count{};
25};
26static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
27
28// This is nn::hid::detail::DebugPadSharedMemoryFormat
29struct 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};
35static_assert(sizeof(DebugPadSharedMemoryFormat) == 0x400,
36 "DebugPadSharedMemoryFormat is an invalid size");
37
38// This is nn::hid::detail::TouchScreenSharedMemoryFormat
39struct 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};
45static_assert(sizeof(TouchScreenSharedMemoryFormat) == 0x3000,
46 "TouchScreenSharedMemoryFormat is an invalid size");
47
48// This is nn::hid::detail::MouseSharedMemoryFormat
49struct 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};
55static_assert(sizeof(MouseSharedMemoryFormat) == 0x400,
56 "MouseSharedMemoryFormat is an invalid size");
57
58// This is nn::hid::detail::KeyboardSharedMemoryFormat
59struct 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};
65static_assert(sizeof(KeyboardSharedMemoryFormat) == 0x400,
66 "KeyboardSharedMemoryFormat is an invalid size");
67
68// This is nn::hid::detail::DigitizerSharedMemoryFormat
69struct DigitizerSharedMemoryFormat {
70 CommonHeader header;
71 INSERT_PADDING_BYTES(0xFE0);
72};
73static_assert(sizeof(DigitizerSharedMemoryFormat) == 0x1000,
74 "DigitizerSharedMemoryFormat is an invalid size");
75
76// This is nn::hid::detail::HomeButtonSharedMemoryFormat
77struct HomeButtonSharedMemoryFormat {
78 CommonHeader header;
79 INSERT_PADDING_BYTES(0x1E0);
80};
81static_assert(sizeof(HomeButtonSharedMemoryFormat) == 0x200,
82 "HomeButtonSharedMemoryFormat is an invalid size");
83
84// This is nn::hid::detail::SleepButtonSharedMemoryFormat
85struct SleepButtonSharedMemoryFormat {
86 CommonHeader header;
87 INSERT_PADDING_BYTES(0x1E0);
88};
89static_assert(sizeof(SleepButtonSharedMemoryFormat) == 0x200,
90 "SleepButtonSharedMemoryFormat is an invalid size");
91
92// This is nn::hid::detail::CaptureButtonSharedMemoryFormat
93struct CaptureButtonSharedMemoryFormat {
94 CommonHeader header;
95 INSERT_PADDING_BYTES(0x1E0);
96};
97static_assert(sizeof(CaptureButtonSharedMemoryFormat) == 0x200,
98 "CaptureButtonSharedMemoryFormat is an invalid size");
99
100// This is nn::hid::detail::InputDetectorSharedMemoryFormat
101struct InputDetectorSharedMemoryFormat {
102 CommonHeader header;
103 INSERT_PADDING_BYTES(0x7E0);
104};
105static_assert(sizeof(InputDetectorSharedMemoryFormat) == 0x800,
106 "InputDetectorSharedMemoryFormat is an invalid size");
107
108// This is nn::hid::detail::UniquePadSharedMemoryFormat
109struct UniquePadSharedMemoryFormat {
110 CommonHeader header;
111 INSERT_PADDING_BYTES(0x3FE0);
112};
113static_assert(sizeof(UniquePadSharedMemoryFormat) == 0x4000,
114 "UniquePadSharedMemoryFormat is an invalid size");
115
116// This is nn::hid::detail::NpadSixAxisSensorLifo
117struct NpadSixAxisSensorLifo {
118 Lifo<Core::HID::SixAxisSensorState, HidEntryCount> lifo;
119};
120
121// This is nn::hid::detail::NpadInternalState
122struct 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};
163static_assert(sizeof(NpadInternalState) == 0x43F8, "NpadInternalState is an invalid size");
164
165// This is nn::hid::detail::NpadSharedMemoryEntry
166struct NpadSharedMemoryEntry {
167 NpadInternalState internal_state;
168 INSERT_PADDING_BYTES(0xC08);
169};
170static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is an invalid size");
171
172// This is nn::hid::detail::NpadSharedMemoryFormat
173struct NpadSharedMemoryFormat {
174 std::array<NpadSharedMemoryEntry, MaxSupportedNpadIdTypes> npad_entry;
175};
176static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000,
177 "NpadSharedMemoryFormat is an invalid size");
178
179// This is nn::hid::detail::GestureSharedMemoryFormat
180struct 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};
186static_assert(sizeof(GestureSharedMemoryFormat) == 0x800,
187 "GestureSharedMemoryFormat is an invalid size");
188
189// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
190struct 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};
198static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
199 "ConsoleSixAxisSensorSharedMemoryFormat is an invalid size");
200
201// This is nn::hid::detail::SharedMemoryFormat
202struct 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};
222static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset");
223static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset");
224static_assert(offsetof(SharedMemoryFormat, mouse) == 0x3400, "mouse has wrong offset");
225static_assert(offsetof(SharedMemoryFormat, keyboard) == 0x3800, "keyboard has wrong offset");
226static_assert(offsetof(SharedMemoryFormat, digitizer) == 0x3C00, "digitizer has wrong offset");
227static_assert(offsetof(SharedMemoryFormat, home_button) == 0x4C00, "home_button has wrong offset");
228static_assert(offsetof(SharedMemoryFormat, sleep_button) == 0x4E00,
229 "sleep_button has wrong offset");
230static_assert(offsetof(SharedMemoryFormat, capture_button) == 0x5000,
231 "capture_button has wrong offset");
232static_assert(offsetof(SharedMemoryFormat, input_detector) == 0x5200,
233 "input_detector has wrong offset");
234static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offset");
235static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset");
236static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset");
237static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset");
238static_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
11namespace Service::HID {
12SharedMemoryHolder::SharedMemoryHolder() {}
13
14SharedMemoryHolder::~SharedMemoryHolder() {
15 Finalize();
16}
17
18Result 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
34void SharedMemoryHolder::Finalize() {
35 if (address != nullptr) {
36 shared_memory->Close();
37 }
38 is_created = false;
39 is_mapped = false;
40 address = nullptr;
41}
42
43bool SharedMemoryHolder::IsMapped() {
44 return is_mapped;
45}
46
47SharedMemoryFormat* SharedMemoryHolder::GetAddress() {
48 return address;
49}
50
51Kernel::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
8namespace Core {
9class System;
10}
11
12namespace Kernel {
13class KSharedMemory;
14}
15
16namespace Service::HID {
17struct SharedMemoryFormat;
18
19// This is nn::hid::detail::SharedMemoryHolder
20class SharedMemoryHolder {
21public:
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
32private:
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
42static_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
10namespace Service::HID {
11
12ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
13 console = hid_core.GetEmulatedConsole();
14}
15
16ConsoleSixAxis::~ConsoleSixAxis() = default;
17
18void ConsoleSixAxis::OnInit() {}
19
20void ConsoleSixAxis::OnRelease() {}
21
22void 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
8namespace Core::HID {
9class EmulatedConsole;
10} // namespace Core::HID
11
12namespace Service::HID {
13class ConsoleSixAxis final : public ControllerBase {
14public:
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
27private:
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
15namespace Service::HID {
16SevenSixAxis::SevenSixAxis(Core::System& system_)
17 : ControllerBase{system_.HIDCore()}, system{system_} {
18 console = hid_core.GetEmulatedConsole();
19}
20
21SevenSixAxis::~SevenSixAxis() = default;
22
23void SevenSixAxis::OnInit() {}
24void SevenSixAxis::OnRelease() {}
25
26void 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
58void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
59 transfer_memory = t_mem;
60}
61
62void 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
12namespace Core {
13class System;
14} // namespace Core
15
16namespace Core::HID {
17class EmulatedConsole;
18} // namespace Core::HID
19
20namespace Service::HID {
21class SevenSixAxis final : public ControllerBase {
22public:
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
41private:
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
14namespace Service::HID {
15
16SixAxis::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
24SixAxis::~SixAxis() = default;
25
26void SixAxis::OnInit() {}
27void SixAxis::OnRelease() {}
28
29void 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
170Result 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
186Result 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
200Result 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
213Result 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
228Result 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
243Result 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
256Result 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
269Result 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
282Result 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}
295Result 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
309Result 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
329Result 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
344SixAxis::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
367const 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
390SixAxis::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
396const 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
402SixAxis::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
411const 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
11namespace Core::HID {
12class EmulatedController;
13} // namespace Core::HID
14
15namespace Service::HID {
16class NPad;
17
18class SixAxis final : public ControllerBase {
19public:
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
60private:
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
9namespace Service::HID {
10
11CaptureButton::CaptureButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13CaptureButton::~CaptureButton() = default;
14
15void CaptureButton::OnInit() {}
16
17void CaptureButton::OnRelease() {}
18
19void 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
8namespace Service::HID {
9
10class CaptureButton final : public ControllerBase {
11public:
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
24private:
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
9namespace Service::HID {
10
11HomeButton::HomeButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13HomeButton::~HomeButton() = default;
14
15void HomeButton::OnInit() {}
16
17void HomeButton::OnRelease() {}
18
19void 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
8namespace Service::HID {
9
10class HomeButton final : public ControllerBase {
11public:
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
24private:
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
9namespace Service::HID {
10
11SleepButton::SleepButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13SleepButton::~SleepButton() = default;
14
15void SleepButton::OnInit() {}
16
17void SleepButton::OnRelease() {}
18
19void 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
8namespace Service::HID {
9
10class SleepButton final : public ControllerBase {
11public:
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
24private:
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
13namespace Service::HID {
14// HW is around 700, value is set to 400 to make it easier to trigger with mouse
15constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
16constexpr f32 angle_threshold = 0.015f; // Threshold in radians
17constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
18constexpr f32 press_delay = 0.5f; // Time in seconds
19constexpr f32 double_tap_delay = 0.35f; // Time in seconds
20
21constexpr f32 Square(s32 num) {
22 return static_cast<f32>(num * num);
23}
24
25Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
26 console = hid_core.GetEmulatedConsole();
27}
28Gesture::~Gesture() = default;
29
30void 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
45void Gesture::OnRelease() {}
46
47void 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
80void 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
92bool 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
115void 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
152void 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
166void 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
200void 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
235void 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
248void 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
274void 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
297void 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
321const GestureState& Gesture::GetLastGestureEntry() const {
322 return shared_memory->gesture_lifo.ReadCurrentEntry().state;
323}
324
325GestureProperties 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
12namespace Core::HID {
13class EmulatedConsole;
14}
15
16namespace Service::HID {
17struct GestureSharedMemoryFormat;
18
19class Gesture final : public ControllerBase {
20public:
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
33private:
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
11namespace Service::HID {
12static constexpr size_t MAX_FINGERS = 16;
13static constexpr size_t MAX_POINTS = 4;
14
15// This is nn::hid::GestureType
16enum 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
30enum class GestureDirection : u32 {
31 None,
32 Left,
33 Up,
34 Right,
35 Down,
36};
37
38// This is nn::hid::GestureAttribute
39struct 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};
47static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
48
49// This is nn::hid::GestureState
50struct 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};
65static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
66
67struct 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
15namespace Service::HID {
16
17TouchScreen::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
23TouchScreen::~TouchScreen() = default;
24
25void TouchScreen::OnInit() {}
26
27void TouchScreen::OnRelease() {}
28
29void 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
127void 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
12namespace Core::HID {
13class EmulatedConsole;
14} // namespace Core::HID
15
16namespace Service::HID {
17struct TouchScreenSharedMemoryFormat;
18
19class TouchScreen final : public ControllerBase {
20public:
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
35private:
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
15namespace Service::HID {
16static constexpr std::size_t MAX_FINGERS = 16;
17static constexpr size_t MAX_POINTS = 4;
18
19// This is nn::hid::GestureType
20enum 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
34enum class GestureDirection : u32 {
35 None,
36 Left,
37 Up,
38 Right,
39 Down,
40};
41
42// This is nn::hid::GestureAttribute
43struct 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};
51static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
52
53// This is nn::hid::GestureState
54struct 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};
69static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
70
71struct 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
82struct 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};
88static_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
9namespace Service::HID {
10
11UniquePad::UniquePad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13UniquePad::~UniquePad() = default;
14
15void UniquePad::OnInit() {}
16
17void UniquePad::OnRelease() {}
18
19void 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
8namespace Service::HID {
9
10class UniquePad final : public ControllerBase {
11public:
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
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID