summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt27
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp20
-rw-r--r--src/core/core.cpp20
-rw-r--r--src/core/core.h13
-rw-r--r--src/core/cpu_manager.cpp23
-rw-r--r--src/core/frontend/applets/controller.cpp45
-rw-r--r--src/core/frontend/applets/controller.h8
-rw-r--r--src/core/frontend/emu_window.cpp98
-rw-r--r--src/core/frontend/emu_window.h30
-rw-r--r--src/core/frontend/framebuffer_layout.cpp7
-rw-r--r--src/core/frontend/framebuffer_layout.h11
-rw-r--r--src/core/frontend/input.h217
-rw-r--r--src/core/hid/emulated_console.cpp232
-rw-r--r--src/core/hid/emulated_console.h190
-rw-r--r--src/core/hid/emulated_controller.cpp1139
-rw-r--r--src/core/hid/emulated_controller.h411
-rw-r--r--src/core/hid/emulated_devices.cpp459
-rw-r--r--src/core/hid/emulated_devices.h210
-rw-r--r--src/core/hid/hid_core.cpp214
-rw-r--r--src/core/hid/hid_core.h82
-rw-r--r--src/core/hid/hid_types.h635
-rw-r--r--src/core/hid/input_converter.cpp383
-rw-r--r--src/core/hid/input_converter.h96
-rw-r--r--src/core/hid/input_interpreter.cpp (renamed from src/core/frontend/input_interpreter.cpp)27
-rw-r--r--src/core/hid/input_interpreter.h (renamed from src/core/frontend/input_interpreter.h)54
-rw-r--r--src/core/hid/motion_input.cpp280
-rw-r--r--src/core/hid/motion_input.h87
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp92
-rw-r--r--src/core/hle/kernel/k_auto_object.h4
-rw-r--r--src/core/hle/kernel/k_class_token.cpp5
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp146
-rw-r--r--src/core/hle/kernel/k_code_memory.h66
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp245
-rw-r--r--src/core/hle/kernel/k_condition_variable.h2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp6
-rw-r--r--src/core/hle/kernel/k_handle_table.h2
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.cpp80
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.h58
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp72
-rw-r--r--src/core/hle/kernel/k_light_lock.h2
-rw-r--r--src/core/hle/kernel/k_memory_block.h20
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp123
-rw-r--r--src/core/hle/kernel/k_page_table.h10
-rw-r--r--src/core/hle/kernel/k_process.cpp30
-rw-r--r--src/core/hle/kernel/k_process.h1
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp101
-rw-r--r--src/core/hle/kernel/k_scheduler.h2
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h10
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h1
-rw-r--r--src/core/hle/kernel/k_server_session.cpp3
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp151
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h32
-rw-r--r--src/core/hle/kernel/k_thread.cpp246
-rw-r--r--src/core/hle/kernel/k_thread.h72
-rw-r--r--src/core/hle/kernel/k_thread_queue.cpp49
-rw-r--r--src/core/hle/kernel/k_thread_queue.h74
-rw-r--r--src/core/hle/kernel/kernel.cpp103
-rw-r--r--src/core/hle/kernel/kernel.h9
-rw-r--r--src/core/hle/kernel/service_thread.cpp33
-rw-r--r--src/core/hle/kernel/svc.cpp309
-rw-r--r--src/core/hle/kernel/svc_wrap.h27
-rw-r--r--src/core/hle/kernel/time_manager.cpp6
-rw-r--r--src/core/hle/service/am/am.cpp14
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp19
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h6
-rw-r--r--src/core/hle/service/am/applets/applets.cpp2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp4
-rw-r--r--src/core/hle/service/caps/caps.h2
-rw-r--r--src/core/hle/service/friend/friend.cpp23
-rw-r--r--src/core/hle/service/glue/glue.cpp4
-rw-r--r--src/core/hle/service/glue/notif.cpp44
-rw-r--r--src/core/hle/service/glue/notif.h25
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.cpp65
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.h44
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h21
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp72
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h82
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp257
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h109
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp59
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h59
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp64
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h63
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp1760
-rw-r--r--src/core/hle/service/hid/controllers/npad.h661
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp6
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h13
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp139
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h86
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp32
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h61
-rw-r--r--src/core/hle/service/hid/hid.cpp463
-rw-r--r--src/core/hle/service/hid/hid.h35
-rw-r--r--src/core/hle/service/hid/ring_lifo.h54
-rw-r--r--src/core/hle/service/ldr/ldr.cpp8
-rw-r--r--src/core/hle/service/ns/ns.cpp23
-rw-r--r--src/core/hle/service/ns/ns.h7
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp69
-rw-r--r--src/core/hle/service/ns/pdm_qry.h33
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h3
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h4
-rw-r--r--src/core/hle/service/pm/pm.cpp47
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp5
-rw-r--r--src/core/memory/cheat_engine.cpp3
-rw-r--r--src/core/perf_stats.h4
114 files changed, 8467 insertions, 3342 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9f0fbba2d..49bed614a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -132,11 +132,23 @@ add_library(core STATIC
132 frontend/emu_window.h 132 frontend/emu_window.h
133 frontend/framebuffer_layout.cpp 133 frontend/framebuffer_layout.cpp
134 frontend/framebuffer_layout.h 134 frontend/framebuffer_layout.h
135 frontend/input_interpreter.cpp
136 frontend/input_interpreter.h
137 frontend/input.h
138 hardware_interrupt_manager.cpp 135 hardware_interrupt_manager.cpp
139 hardware_interrupt_manager.h 136 hardware_interrupt_manager.h
137 hid/emulated_console.cpp
138 hid/emulated_console.h
139 hid/emulated_controller.cpp
140 hid/emulated_controller.h
141 hid/emulated_devices.cpp
142 hid/emulated_devices.h
143 hid/hid_core.cpp
144 hid/hid_core.h
145 hid/hid_types.h
146 hid/input_converter.cpp
147 hid/input_converter.h
148 hid/input_interpreter.cpp
149 hid/input_interpreter.h
150 hid/motion_input.cpp
151 hid/motion_input.h
140 hle/api_version.h 152 hle/api_version.h
141 hle/ipc.h 153 hle/ipc.h
142 hle/ipc_helpers.h 154 hle/ipc_helpers.h
@@ -167,12 +179,15 @@ add_library(core STATIC
167 hle/kernel/k_client_port.h 179 hle/kernel/k_client_port.h
168 hle/kernel/k_client_session.cpp 180 hle/kernel/k_client_session.cpp
169 hle/kernel/k_client_session.h 181 hle/kernel/k_client_session.h
182 hle/kernel/k_code_memory.cpp
183 hle/kernel/k_code_memory.h
170 hle/kernel/k_condition_variable.cpp 184 hle/kernel/k_condition_variable.cpp
171 hle/kernel/k_condition_variable.h 185 hle/kernel/k_condition_variable.h
172 hle/kernel/k_event.cpp 186 hle/kernel/k_event.cpp
173 hle/kernel/k_event.h 187 hle/kernel/k_event.h
174 hle/kernel/k_handle_table.cpp 188 hle/kernel/k_handle_table.cpp
175 hle/kernel/k_handle_table.h 189 hle/kernel/k_handle_table.h
190 hle/kernel/k_light_condition_variable.cpp
176 hle/kernel/k_light_condition_variable.h 191 hle/kernel/k_light_condition_variable.h
177 hle/kernel/k_light_lock.cpp 192 hle/kernel/k_light_lock.cpp
178 hle/kernel/k_light_lock.h 193 hle/kernel/k_light_lock.h
@@ -225,6 +240,7 @@ add_library(core STATIC
225 hle/kernel/k_system_control.h 240 hle/kernel/k_system_control.h
226 hle/kernel/k_thread.cpp 241 hle/kernel/k_thread.cpp
227 hle/kernel/k_thread.h 242 hle/kernel/k_thread.h
243 hle/kernel/k_thread_queue.cpp
228 hle/kernel/k_thread_queue.h 244 hle/kernel/k_thread_queue.h
229 hle/kernel/k_trace.h 245 hle/kernel/k_trace.h
230 hle/kernel/k_transfer_memory.cpp 246 hle/kernel/k_transfer_memory.cpp
@@ -396,12 +412,15 @@ add_library(core STATIC
396 hle/service/glue/glue.h 412 hle/service/glue/glue.h
397 hle/service/glue/glue_manager.cpp 413 hle/service/glue/glue_manager.cpp
398 hle/service/glue/glue_manager.h 414 hle/service/glue/glue_manager.h
415 hle/service/glue/notif.cpp
416 hle/service/glue/notif.h
399 hle/service/grc/grc.cpp 417 hle/service/grc/grc.cpp
400 hle/service/grc/grc.h 418 hle/service/grc/grc.h
401 hle/service/hid/hid.cpp 419 hle/service/hid/hid.cpp
402 hle/service/hid/hid.h 420 hle/service/hid/hid.h
403 hle/service/hid/irs.cpp 421 hle/service/hid/irs.cpp
404 hle/service/hid/irs.h 422 hle/service/hid/irs.h
423 hle/service/hid/ring_lifo.h
405 hle/service/hid/xcd.cpp 424 hle/service/hid/xcd.cpp
406 hle/service/hid/xcd.h 425 hle/service/hid/xcd.h
407 hle/service/hid/errors.h 426 hle/service/hid/errors.h
@@ -466,6 +485,8 @@ add_library(core STATIC
466 hle/service/ns/language.h 485 hle/service/ns/language.h
467 hle/service/ns/ns.cpp 486 hle/service/ns/ns.cpp
468 hle/service/ns/ns.h 487 hle/service/ns/ns.h
488 hle/service/ns/pdm_qry.cpp
489 hle/service/ns/pdm_qry.h
469 hle/service/ns/pl_u.cpp 490 hle/service/ns/pl_u.cpp
470 hle/service/ns/pl_u.h 491 hle/service/ns/pl_u.h
471 hle/service/nvdrv/devices/nvdevice.h 492 hle/service/nvdrv/devices/nvdevice.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 4e73cc03a..56836bd05 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -86,6 +86,26 @@ public:
86 num_instructions, MemoryReadCode(pc)); 86 num_instructions, MemoryReadCode(pc));
87 } 87 }
88 88
89 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
90 VAddr value) override {
91 switch (op) {
92 case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: {
93 static constexpr u64 ICACHE_LINE_SIZE = 64;
94
95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
96 parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
97 break;
98 }
99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
100 parent.ClearInstructionCache();
101 break;
102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
103 default:
104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
105 break;
106 }
107 }
108
89 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 109 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
90 switch (exception) { 110 switch (exception) {
91 case Dynarmic::A64::Exception::WaitForInterrupt: 111 case Dynarmic::A64::Exception::WaitForInterrupt:
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 07448fd29..aa96f709b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,6 +27,7 @@
27#include "core/file_sys/vfs_concat.h" 27#include "core/file_sys/vfs_concat.h"
28#include "core/file_sys/vfs_real.h" 28#include "core/file_sys/vfs_real.h"
29#include "core/hardware_interrupt_manager.h" 29#include "core/hardware_interrupt_manager.h"
30#include "core/hid/hid_core.h"
30#include "core/hle/kernel/k_process.h" 31#include "core/hle/kernel/k_process.h"
31#include "core/hle/kernel/k_scheduler.h" 32#include "core/hle/kernel/k_scheduler.h"
32#include "core/hle/kernel/kernel.h" 33#include "core/hle/kernel/kernel.h"
@@ -126,7 +127,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
126 127
127struct System::Impl { 128struct System::Impl {
128 explicit Impl(System& system) 129 explicit Impl(System& system)
129 : kernel{system}, fs_controller{system}, memory{system}, 130 : kernel{system}, fs_controller{system}, memory{system}, hid_core{},
130 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} 131 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
131 132
132 SystemResultStatus Run() { 133 SystemResultStatus Run() {
@@ -391,6 +392,7 @@ struct System::Impl {
391 std::unique_ptr<Hardware::InterruptManager> interrupt_manager; 392 std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
392 std::unique_ptr<Core::DeviceMemory> device_memory; 393 std::unique_ptr<Core::DeviceMemory> device_memory;
393 Core::Memory::Memory memory; 394 Core::Memory::Memory memory;
395 Core::HID::HIDCore hid_core;
394 CpuManager cpu_manager; 396 CpuManager cpu_manager;
395 std::atomic_bool is_powered_on{}; 397 std::atomic_bool is_powered_on{};
396 bool exit_lock = false; 398 bool exit_lock = false;
@@ -519,12 +521,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
519 return impl->kernel.CurrentPhysicalCore().ArmInterface(); 521 return impl->kernel.CurrentPhysicalCore().ArmInterface();
520} 522}
521 523
522std::size_t System::CurrentCoreIndex() const {
523 std::size_t core = impl->kernel.GetCurrentHostThreadID();
524 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
525 return core;
526}
527
528Kernel::PhysicalCore& System::CurrentPhysicalCore() { 524Kernel::PhysicalCore& System::CurrentPhysicalCore() {
529 return impl->kernel.CurrentPhysicalCore(); 525 return impl->kernel.CurrentPhysicalCore();
530} 526}
@@ -615,6 +611,14 @@ const Kernel::KernelCore& System::Kernel() const {
615 return impl->kernel; 611 return impl->kernel;
616} 612}
617 613
614HID::HIDCore& System::HIDCore() {
615 return impl->hid_core;
616}
617
618const HID::HIDCore& System::HIDCore() const {
619 return impl->hid_core;
620}
621
618Timing::CoreTiming& System::CoreTiming() { 622Timing::CoreTiming& System::CoreTiming() {
619 return impl->core_timing; 623 return impl->core_timing;
620} 624}
@@ -825,8 +829,6 @@ void System::ApplySettings() {
825 if (IsPoweredOn()) { 829 if (IsPoweredOn()) {
826 Renderer().RefreshBaseSettings(); 830 Renderer().RefreshBaseSettings();
827 } 831 }
828
829 Service::HID::ReloadInputDevices();
830} 832}
831 833
832} // namespace Core 834} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index 01bc0a2c7..52ff90359 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -89,6 +89,10 @@ namespace Core::Hardware {
89class InterruptManager; 89class InterruptManager;
90} 90}
91 91
92namespace Core::HID {
93class HIDCore;
94}
95
92namespace Core { 96namespace Core {
93 97
94class ARM_Interface; 98class ARM_Interface;
@@ -204,9 +208,6 @@ public:
204 /// Gets an ARM interface to the CPU core that is currently running 208 /// Gets an ARM interface to the CPU core that is currently running
205 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; 209 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
206 210
207 /// Gets the index of the currently running CPU core
208 [[nodiscard]] std::size_t CurrentCoreIndex() const;
209
210 /// Gets the physical core for the CPU core that is currently running 211 /// Gets the physical core for the CPU core that is currently running
211 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); 212 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
212 213
@@ -285,6 +286,12 @@ public:
285 /// Provides a constant reference to the kernel instance. 286 /// Provides a constant reference to the kernel instance.
286 [[nodiscard]] const Kernel::KernelCore& Kernel() const; 287 [[nodiscard]] const Kernel::KernelCore& Kernel() const;
287 288
289 /// Gets a mutable reference to the HID interface.
290 [[nodiscard]] HID::HIDCore& HIDCore();
291
292 /// Gets an immutable reference to the HID interface.
293 [[nodiscard]] const HID::HIDCore& HIDCore() const;
294
288 /// Provides a reference to the internal PerfStats instance. 295 /// Provides a reference to the internal PerfStats instance.
289 [[nodiscard]] Core::PerfStats& GetPerfStats(); 296 [[nodiscard]] Core::PerfStats& GetPerfStats();
290 297
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 5d43c6e5d..cbcc54891 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -117,17 +117,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
117 physical_core = &kernel.CurrentPhysicalCore(); 117 physical_core = &kernel.CurrentPhysicalCore();
118 } 118 }
119 system.ExitDynarmicProfile(); 119 system.ExitDynarmicProfile();
120 physical_core->ArmInterface().ClearExclusiveState(); 120 {
121 kernel.CurrentScheduler()->RescheduleCurrentCore(); 121 Kernel::KScopedDisableDispatch dd(kernel);
122 physical_core->ArmInterface().ClearExclusiveState();
123 }
122 } 124 }
123} 125}
124 126
125void CpuManager::MultiCoreRunIdleThread() { 127void CpuManager::MultiCoreRunIdleThread() {
126 auto& kernel = system.Kernel(); 128 auto& kernel = system.Kernel();
127 while (true) { 129 while (true) {
128 auto& physical_core = kernel.CurrentPhysicalCore(); 130 Kernel::KScopedDisableDispatch dd(kernel);
129 physical_core.Idle(); 131 kernel.CurrentPhysicalCore().Idle();
130 kernel.CurrentScheduler()->RescheduleCurrentCore();
131 } 132 }
132} 133}
133 134
@@ -135,12 +136,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
135 auto& kernel = system.Kernel(); 136 auto& kernel = system.Kernel();
136 kernel.CurrentScheduler()->OnThreadStart(); 137 kernel.CurrentScheduler()->OnThreadStart();
137 while (true) { 138 while (true) {
138 auto core = kernel.GetCurrentHostThreadID(); 139 auto core = kernel.CurrentPhysicalCoreIndex();
139 auto& scheduler = *kernel.CurrentScheduler(); 140 auto& scheduler = *kernel.CurrentScheduler();
140 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 141 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
141 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); 142 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
142 ASSERT(scheduler.ContextSwitchPending()); 143 ASSERT(scheduler.ContextSwitchPending());
143 ASSERT(core == kernel.GetCurrentHostThreadID()); 144 ASSERT(core == kernel.CurrentPhysicalCoreIndex());
144 scheduler.RescheduleCurrentCore(); 145 scheduler.RescheduleCurrentCore();
145 } 146 }
146} 147}
@@ -346,13 +347,9 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
346 sc_sync_first_use = false; 347 sc_sync_first_use = false;
347 } 348 }
348 349
349 // Abort if emulation was killed before the session really starts 350 // Emulation was stopped
350 if (!system.IsPoweredOn()) {
351 return;
352 }
353
354 if (stop_token.stop_requested()) { 351 if (stop_token.stop_requested()) {
355 break; 352 return;
356 } 353 }
357 354
358 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); 355 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 03bbedf8b..6dbd38ffa 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -5,16 +5,15 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/frontend/applets/controller.h" 7#include "core/frontend/applets/controller.h"
8#include "core/hle/service/hid/controllers/npad.h" 8#include "core/hid/emulated_controller.h"
9#include "core/hle/service/hid/hid.h" 9#include "core/hid/hid_core.h"
10#include "core/hle/service/sm/sm.h" 10#include "core/hid/hid_types.h"
11 11
12namespace Core::Frontend { 12namespace Core::Frontend {
13 13
14ControllerApplet::~ControllerApplet() = default; 14ControllerApplet::~ControllerApplet() = default;
15 15
16DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_) 16DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_core{hid_core_} {}
17 : service_manager{service_manager_} {}
18 17
19DefaultControllerApplet::~DefaultControllerApplet() = default; 18DefaultControllerApplet::~DefaultControllerApplet() = default;
20 19
@@ -22,24 +21,20 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
22 const ControllerParameters& parameters) const { 21 const ControllerParameters& parameters) const {
23 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); 22 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
24 23
25 auto& npad =
26 service_manager.GetService<Service::HID::Hid>("hid")
27 ->GetAppletResource()
28 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
29
30 auto& players = Settings::values.players.GetValue();
31
32 const std::size_t min_supported_players = 24 const std::size_t min_supported_players =
33 parameters.enable_single_mode ? 1 : parameters.min_players; 25 parameters.enable_single_mode ? 1 : parameters.min_players;
34 26
35 // Disconnect Handheld first. 27 // Disconnect Handheld first.
36 npad.DisconnectNpadAtIndex(8); 28 auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
29 handheld->Disconnect();
37 30
38 // Deduce the best configuration based on the input parameters. 31 // Deduce the best configuration based on the input parameters.
39 for (std::size_t index = 0; index < players.size() - 2; ++index) { 32 for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
33 auto* controller = hid_core.GetEmulatedControllerByIndex(index);
34
40 // First, disconnect all controllers regardless of the value of keep_controllers_connected. 35 // First, disconnect all controllers regardless of the value of keep_controllers_connected.
41 // This makes it easy to connect the desired controllers. 36 // This makes it easy to connect the desired controllers.
42 npad.DisconnectNpadAtIndex(index); 37 controller->Disconnect();
43 38
44 // Only connect the minimum number of required players. 39 // Only connect the minimum number of required players.
45 if (index >= min_supported_players) { 40 if (index >= min_supported_players) {
@@ -49,27 +44,27 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
49 // Connect controllers based on the following priority list from highest to lowest priority: 44 // Connect controllers based on the following priority list from highest to lowest priority:
50 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld 45 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
51 if (parameters.allow_pro_controller) { 46 if (parameters.allow_pro_controller) {
52 npad.AddNewControllerAt( 47 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
53 npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index); 48 controller->Connect();
54 } else if (parameters.allow_dual_joycons) { 49 } else if (parameters.allow_dual_joycons) {
55 npad.AddNewControllerAt( 50 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
56 npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index); 51 controller->Connect();
57 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) { 52 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
58 // Assign left joycons to even player indices and right joycons to odd player indices. 53 // Assign left joycons to even player indices and right joycons to odd player indices.
59 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and 54 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
60 // a right Joycon for Player 2 in 2 Player Assist mode. 55 // a right Joycon for Player 2 in 2 Player Assist mode.
61 if (index % 2 == 0) { 56 if (index % 2 == 0) {
62 npad.AddNewControllerAt( 57 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
63 npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index); 58 controller->Connect();
64 } else { 59 } else {
65 npad.AddNewControllerAt( 60 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
66 npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); 61 controller->Connect();
67 } 62 }
68 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && 63 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
69 !Settings::values.use_docked_mode.GetValue()) { 64 !Settings::values.use_docked_mode.GetValue()) {
70 // We should *never* reach here under any normal circumstances. 65 // We should *never* reach here under any normal circumstances.
71 npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), 66 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
72 index); 67 controller->Connect();
73 } else { 68 } else {
74 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); 69 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
75 } 70 }
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index b0626a0f9..014bc8901 100644
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -8,8 +8,8 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace Service::SM { 11namespace Core::HID {
12class ServiceManager; 12class HIDCore;
13} 13}
14 14
15namespace Core::Frontend { 15namespace Core::Frontend {
@@ -44,14 +44,14 @@ public:
44 44
45class DefaultControllerApplet final : public ControllerApplet { 45class DefaultControllerApplet final : public ControllerApplet {
46public: 46public:
47 explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_); 47 explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
48 ~DefaultControllerApplet() override; 48 ~DefaultControllerApplet() override;
49 49
50 void ReconfigureControllers(std::function<void()> callback, 50 void ReconfigureControllers(std::function<void()> callback,
51 const ControllerParameters& parameters) const override; 51 const ControllerParameters& parameters) const override;
52 52
53private: 53private:
54 Service::SM::ServiceManager& service_manager; 54 HID::HIDCore& hid_core;
55}; 55};
56 56
57} // namespace Core::Frontend 57} // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index e1f7e5886..57c6ffc43 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -3,87 +3,23 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mutex> 5#include <mutex>
6#include "common/settings.h"
7#include "core/frontend/emu_window.h" 6#include "core/frontend/emu_window.h"
8#include "core/frontend/input.h"
9 7
10namespace Core::Frontend { 8namespace Core::Frontend {
11 9
12GraphicsContext::~GraphicsContext() = default; 10GraphicsContext::~GraphicsContext() = default;
13 11
14class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
15 public std::enable_shared_from_this<TouchState> {
16public:
17 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
18 return std::make_unique<Device>(shared_from_this());
19 }
20
21 std::mutex mutex;
22
23 Input::TouchStatus status;
24
25private:
26 class Device : public Input::TouchDevice {
27 public:
28 explicit Device(std::weak_ptr<TouchState>&& touch_state_) : touch_state(touch_state_) {}
29 Input::TouchStatus GetStatus() const override {
30 if (auto state = touch_state.lock()) {
31 std::lock_guard guard{state->mutex};
32 return state->status;
33 }
34 return {};
35 }
36
37 private:
38 std::weak_ptr<TouchState> touch_state;
39 };
40};
41
42EmuWindow::EmuWindow() { 12EmuWindow::EmuWindow() {
43 // TODO: Find a better place to set this. 13 // TODO: Find a better place to set this.
44 config.min_client_area_size = 14 config.min_client_area_size =
45 std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height); 15 std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
46 active_config = config; 16 active_config = config;
47 touch_state = std::make_shared<TouchState>();
48 Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
49}
50
51EmuWindow::~EmuWindow() {
52 Input::UnregisterFactory<Input::TouchDevice>("emu_window");
53}
54
55/**
56 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
57 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
58 * @param framebuffer_x Framebuffer x-coordinate to check
59 * @param framebuffer_y Framebuffer y-coordinate to check
60 * @return True if the coordinates are within the touchpad, otherwise false
61 */
62static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, u32 framebuffer_x,
63 u32 framebuffer_y) {
64 return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom &&
65 framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right);
66}
67
68std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
69 new_x = std::max(new_x, framebuffer_layout.screen.left);
70 new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
71
72 new_y = std::max(new_y, framebuffer_layout.screen.top);
73 new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
74
75 return std::make_pair(new_x, new_y);
76} 17}
77 18
78void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) { 19EmuWindow::~EmuWindow() {}
79 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
80 return;
81 }
82 if (id >= touch_state->status.size()) {
83 return;
84 }
85 20
86 std::lock_guard guard{touch_state->mutex}; 21std::pair<f32, f32> EmuWindow::MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const {
22 std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
87 const float x = 23 const float x =
88 static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / 24 static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
89 static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); 25 static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
@@ -91,31 +27,17 @@ void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
91 static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / 27 static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
92 static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); 28 static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
93 29
94 touch_state->status[id] = std::make_tuple(x, y, true); 30 return std::make_pair(x, y);
95}
96
97void EmuWindow::TouchReleased(size_t id) {
98 if (id >= touch_state->status.size()) {
99 return;
100 }
101 std::lock_guard guard{touch_state->mutex};
102 touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
103} 31}
104 32
105void EmuWindow::TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id) { 33std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
106 if (id >= touch_state->status.size()) { 34 new_x = std::max(new_x, framebuffer_layout.screen.left);
107 return; 35 new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
108 }
109
110 if (!std::get<2>(touch_state->status[id])) {
111 return;
112 }
113 36
114 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { 37 new_y = std::max(new_y, framebuffer_layout.screen.top);
115 std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); 38 new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
116 }
117 39
118 TouchPressed(framebuffer_x, framebuffer_y, id); 40 return std::make_pair(new_x, new_y);
119} 41}
120 42
121void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) { 43void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 8a86a1d27..e413a520a 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -113,28 +113,6 @@ public:
113 virtual bool IsShown() const = 0; 113 virtual bool IsShown() const = 0;
114 114
115 /** 115 /**
116 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
117 * @param framebuffer_x Framebuffer x-coordinate that was pressed
118 * @param framebuffer_y Framebuffer y-coordinate that was pressed
119 * @param id Touch event ID
120 */
121 void TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id);
122
123 /**
124 * Signal that a touch released event has occurred (e.g. mouse click released)
125 * @param id Touch event ID
126 */
127 void TouchReleased(size_t id);
128
129 /**
130 * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
131 * @param framebuffer_x Framebuffer x-coordinate
132 * @param framebuffer_y Framebuffer y-coordinate
133 * @param id Touch event ID
134 */
135 void TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id);
136
137 /**
138 * Returns currently active configuration. 116 * Returns currently active configuration.
139 * @note Accesses to the returned object need not be consistent because it may be modified in 117 * @note Accesses to the returned object need not be consistent because it may be modified in
140 * another thread 118 * another thread
@@ -212,6 +190,11 @@ protected:
212 client_area_height = size.second; 190 client_area_height = size.second;
213 } 191 }
214 192
193 /**
194 * Converts a screen postion into the equivalent touchscreen position.
195 */
196 std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
197
215 WindowSystemInfo window_info; 198 WindowSystemInfo window_info;
216 199
217private: 200private:
@@ -237,9 +220,6 @@ private:
237 WindowConfig config; ///< Internal configuration (changes pending for being applied in 220 WindowConfig config; ///< Internal configuration (changes pending for being applied in
238 /// ProcessConfigurationChanges) 221 /// ProcessConfigurationChanges)
239 WindowConfig active_config; ///< Internal active configuration 222 WindowConfig active_config; ///< Internal active configuration
240
241 class TouchState;
242 std::shared_ptr<TouchState> touch_state;
243}; 223};
244 224
245} // namespace Core::Frontend 225} // namespace Core::Frontend
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 4b58b672a..26a5b12aa 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -25,7 +25,12 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
25 ASSERT(height > 0); 25 ASSERT(height > 0);
26 // The drawing code needs at least somewhat valid values for both screens 26 // The drawing code needs at least somewhat valid values for both screens
27 // so just calculate them both even if the other isn't showing. 27 // so just calculate them both even if the other isn't showing.
28 FramebufferLayout res{width, height, false, {}}; 28 FramebufferLayout res{
29 .width = width,
30 .height = height,
31 .screen = {},
32 .is_srgb = false,
33 };
29 34
30 const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width); 35 const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
31 const float emulation_aspect_ratio = EmulationAspectRatio( 36 const float emulation_aspect_ratio = EmulationAspectRatio(
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 2e36c0163..8e341e4e2 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -35,17 +35,8 @@ enum class AspectRatio {
35struct FramebufferLayout { 35struct FramebufferLayout {
36 u32 width{ScreenUndocked::Width}; 36 u32 width{ScreenUndocked::Width};
37 u32 height{ScreenUndocked::Height}; 37 u32 height{ScreenUndocked::Height};
38 bool is_srgb{};
39
40 Common::Rectangle<u32> screen; 38 Common::Rectangle<u32> screen;
41 39 bool is_srgb{};
42 /**
43 * Returns the ration of pixel size of the screen, compared to the native size of the undocked
44 * Switch screen.
45 */
46 float GetScalingRatio() const {
47 return static_cast<float>(screen.GetWidth()) / ScreenUndocked::Width;
48 }
49}; 40};
50 41
51/** 42/**
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
deleted file mode 100644
index f1747c5b2..000000000
--- a/src/core/frontend/input.h
+++ /dev/null
@@ -1,217 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <string>
10#include <tuple>
11#include <unordered_map>
12#include <utility>
13#include "common/logging/log.h"
14#include "common/param_package.h"
15#include "common/quaternion.h"
16#include "common/vector_math.h"
17
18namespace Input {
19
20enum class AnalogDirection : u8 {
21 RIGHT,
22 LEFT,
23 UP,
24 DOWN,
25};
26struct AnalogProperties {
27 float deadzone;
28 float range;
29 float threshold;
30};
31template <typename StatusType>
32struct InputCallback {
33 std::function<void(StatusType)> on_change;
34};
35
36/// An abstract class template for an input device (a button, an analog input, etc.).
37template <typename StatusType>
38class InputDevice {
39public:
40 virtual ~InputDevice() = default;
41 virtual StatusType GetStatus() const {
42 return {};
43 }
44 virtual StatusType GetRawStatus() const {
45 return GetStatus();
46 }
47 virtual AnalogProperties GetAnalogProperties() const {
48 return {};
49 }
50 virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
51 return {};
52 }
53 virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low,
54 [[maybe_unused]] f32 amp_high,
55 [[maybe_unused]] f32 freq_high) const {
56 return {};
57 }
58 void SetCallback(InputCallback<StatusType> callback_) {
59 callback = std::move(callback_);
60 }
61 void TriggerOnChange() {
62 if (callback.on_change) {
63 callback.on_change(GetStatus());
64 }
65 }
66
67private:
68 InputCallback<StatusType> callback;
69};
70
71/// An abstract class template for a factory that can create input devices.
72template <typename InputDeviceType>
73class Factory {
74public:
75 virtual ~Factory() = default;
76 virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
77};
78
79namespace Impl {
80
81template <typename InputDeviceType>
82using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
83
84template <typename InputDeviceType>
85struct FactoryList {
86 static FactoryListType<InputDeviceType> list;
87};
88
89template <typename InputDeviceType>
90FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
91
92} // namespace Impl
93
94/**
95 * Registers an input device factory.
96 * @tparam InputDeviceType the type of input devices the factory can create
97 * @param name the name of the factory. Will be used to match the "engine" parameter when creating
98 * a device
99 * @param factory the factory object to register
100 */
101template <typename InputDeviceType>
102void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
103 auto pair = std::make_pair(name, std::move(factory));
104 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
105 LOG_ERROR(Input, "Factory '{}' already registered", name);
106 }
107}
108
109/**
110 * Unregisters an input device factory.
111 * @tparam InputDeviceType the type of input devices the factory can create
112 * @param name the name of the factory to unregister
113 */
114template <typename InputDeviceType>
115void UnregisterFactory(const std::string& name) {
116 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
117 LOG_ERROR(Input, "Factory '{}' not registered", name);
118 }
119}
120
121/**
122 * Create an input device from given paramters.
123 * @tparam InputDeviceType the type of input devices to create
124 * @param params a serialized ParamPackage string contains all parameters for creating the device
125 */
126template <typename InputDeviceType>
127std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
128 const Common::ParamPackage package(params);
129 const std::string engine = package.Get("engine", "null");
130 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
131 const auto pair = factory_list.find(engine);
132 if (pair == factory_list.end()) {
133 if (engine != "null") {
134 LOG_ERROR(Input, "Unknown engine name: {}", engine);
135 }
136 return std::make_unique<InputDeviceType>();
137 }
138 return pair->second->Create(package);
139}
140
141/**
142 * A button device is an input device that returns bool as status.
143 * true for pressed; false for released.
144 */
145using ButtonDevice = InputDevice<bool>;
146
147/**
148 * An analog device is an input device that returns a tuple of x and y coordinates as status. The
149 * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
150 * direction
151 */
152using AnalogDevice = InputDevice<std::tuple<float, float>>;
153
154/**
155 * A vibration device is an input device that returns an unsigned byte as status.
156 * It represents whether the vibration device supports vibration or not.
157 * If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
158 */
159using VibrationDevice = InputDevice<u8>;
160
161/**
162 * A motion status is an object that returns a tuple of accelerometer state vector,
163 * gyroscope state vector, rotation state vector, orientation state matrix and quaterion state
164 * vector.
165 *
166 * For both 3D vectors:
167 * x+ is the same direction as RIGHT on D-pad.
168 * y+ is normal to the touch screen, pointing outward.
169 * z+ is the same direction as UP on D-pad.
170 *
171 * For accelerometer state vector
172 * Units: g (gravitational acceleration)
173 *
174 * For gyroscope state vector:
175 * Orientation is determined by right-hand rule.
176 * Units: deg/sec
177 *
178 * For rotation state vector
179 * Units: rotations
180 *
181 * For orientation state matrix
182 * x vector
183 * y vector
184 * z vector
185 *
186 * For quaternion state vector
187 * xyz vector
188 * w float
189 */
190using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
191 std::array<Common::Vec3f, 3>, Common::Quaternion<f32>>;
192
193/**
194 * A motion device is an input device that returns a motion status object
195 */
196using MotionDevice = InputDevice<MotionStatus>;
197
198/**
199 * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
200 * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
201 * pressed.
202 */
203using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
204
205/**
206 * A touch device is an input device that returns a touch status object
207 */
208using TouchDevice = InputDevice<TouchStatus>;
209
210/**
211 * A mouse device is an input device that returns a tuple of two floats and four ints.
212 * The first two floats are X and Y device coordinates of the mouse (from 0-1).
213 * The s32s are the mouse wheel.
214 */
215using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
216
217} // namespace Input
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
new file mode 100644
index 000000000..685ec080c
--- /dev/null
+++ b/src/core/hid/emulated_console.cpp
@@ -0,0 +1,232 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/settings.h"
6#include "core/hid/emulated_console.h"
7#include "core/hid/input_converter.h"
8
9namespace Core::HID {
10EmulatedConsole::EmulatedConsole() = default;
11
12EmulatedConsole::~EmulatedConsole() = default;
13
14void EmulatedConsole::ReloadFromSettings() {
15 // Using first motion device from player 1. No need to assign any unique config at the moment
16 const auto& player = Settings::values.players.GetValue()[0];
17 motion_params = Common::ParamPackage(player.motions[0]);
18
19 ReloadInput();
20}
21
22void EmulatedConsole::SetTouchParams() {
23 // TODO(german77): Support any number of fingers
24 std::size_t index = 0;
25
26 // Hardcode mouse, touchscreen and cemuhook parameters
27 if (!Settings::values.mouse_enabled) {
28 // We can't use mouse as touch if native mouse is enabled
29 touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
30 }
31 touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
32 touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
33 touch_params[index++] =
34 Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
35 touch_params[index++] =
36 Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
37
38 const auto button_index =
39 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
40 const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
41
42 // Map the rest of the fingers from touch from button configuration
43 for (const auto& config_entry : touch_buttons) {
44 if (index >= touch_params.size()) {
45 continue;
46 }
47 Common::ParamPackage params{config_entry};
48 Common::ParamPackage touch_button_params;
49 const int x = params.Get("x", 0);
50 const int y = params.Get("y", 0);
51 params.Erase("x");
52 params.Erase("y");
53 touch_button_params.Set("engine", "touch_from_button");
54 touch_button_params.Set("button", params.Serialize());
55 touch_button_params.Set("x", x);
56 touch_button_params.Set("y", y);
57 touch_button_params.Set("touch_id", static_cast<int>(index));
58 touch_params[index] = touch_button_params;
59 index++;
60 }
61}
62
63void EmulatedConsole::ReloadInput() {
64 // If you load any device here add the equivalent to the UnloadInput() function
65 SetTouchParams();
66
67 motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
68 if (motion_devices) {
69 motion_devices->SetCallback({
70 .on_change =
71 [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
72 });
73 }
74
75 // Unique index for identifying touch device source
76 std::size_t index = 0;
77 for (auto& touch_device : touch_devices) {
78 touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]);
79 if (!touch_device) {
80 continue;
81 }
82 touch_device->SetCallback({
83 .on_change =
84 [this, index](const Common::Input::CallbackStatus& callback) {
85 SetTouch(callback, index);
86 },
87 });
88 index++;
89 }
90}
91
92void EmulatedConsole::UnloadInput() {
93 motion_devices.reset();
94 for (auto& touch : touch_devices) {
95 touch.reset();
96 }
97}
98
99void EmulatedConsole::EnableConfiguration() {
100 is_configuring = true;
101 SaveCurrentConfig();
102}
103
104void EmulatedConsole::DisableConfiguration() {
105 is_configuring = false;
106}
107
108bool EmulatedConsole::IsConfiguring() const {
109 return is_configuring;
110}
111
112void EmulatedConsole::SaveCurrentConfig() {
113 if (!is_configuring) {
114 return;
115 }
116}
117
118void EmulatedConsole::RestoreConfig() {
119 if (!is_configuring) {
120 return;
121 }
122 ReloadFromSettings();
123}
124
125Common::ParamPackage EmulatedConsole::GetMotionParam() const {
126 return motion_params;
127}
128
129void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
130 motion_params = param;
131 ReloadInput();
132}
133
134void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
135 std::lock_guard lock{mutex};
136 auto& raw_status = console.motion_values.raw_status;
137 auto& emulated = console.motion_values.emulated;
138
139 raw_status = TransformToMotion(callback);
140 emulated.SetAcceleration(Common::Vec3f{
141 raw_status.accel.x.value,
142 raw_status.accel.y.value,
143 raw_status.accel.z.value,
144 });
145 emulated.SetGyroscope(Common::Vec3f{
146 raw_status.gyro.x.value,
147 raw_status.gyro.y.value,
148 raw_status.gyro.z.value,
149 });
150 emulated.UpdateRotation(raw_status.delta_timestamp);
151 emulated.UpdateOrientation(raw_status.delta_timestamp);
152
153 if (is_configuring) {
154 TriggerOnChange(ConsoleTriggerType::Motion);
155 return;
156 }
157
158 auto& motion = console.motion_state;
159 motion.accel = emulated.GetAcceleration();
160 motion.gyro = emulated.GetGyroscope();
161 motion.rotation = emulated.GetGyroscope();
162 motion.orientation = emulated.GetOrientation();
163 motion.quaternion = emulated.GetQuaternion();
164 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
165
166 TriggerOnChange(ConsoleTriggerType::Motion);
167}
168
169void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
170 if (index >= console.touch_values.size()) {
171 return;
172 }
173 std::lock_guard lock{mutex};
174
175 console.touch_values[index] = TransformToTouch(callback);
176
177 if (is_configuring) {
178 TriggerOnChange(ConsoleTriggerType::Touch);
179 return;
180 }
181
182 // TODO(german77): Remap touch id in sequential order
183 console.touch_state[index] = {
184 .position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
185 .id = static_cast<u32>(console.touch_values[index].id),
186 .pressed = console.touch_values[index].pressed.value,
187 };
188
189 TriggerOnChange(ConsoleTriggerType::Touch);
190}
191
192ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
193 return console.motion_values;
194}
195
196TouchValues EmulatedConsole::GetTouchValues() const {
197 return console.touch_values;
198}
199
200ConsoleMotion EmulatedConsole::GetMotion() const {
201 return console.motion_state;
202}
203
204TouchFingerState EmulatedConsole::GetTouch() const {
205 return console.touch_state;
206}
207
208void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
209 for (const auto& poller_pair : callback_list) {
210 const ConsoleUpdateCallback& poller = poller_pair.second;
211 if (poller.on_change) {
212 poller.on_change(type);
213 }
214 }
215}
216
217int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
218 std::lock_guard lock{mutex};
219 callback_list.insert_or_assign(last_callback_key, update_callback);
220 return last_callback_key++;
221}
222
223void EmulatedConsole::DeleteCallback(int key) {
224 std::lock_guard lock{mutex};
225 const auto& iterator = callback_list.find(key);
226 if (iterator == callback_list.end()) {
227 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
228 return;
229 }
230 callback_list.erase(iterator);
231}
232} // namespace Core::HID
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
new file mode 100644
index 000000000..3afd284d5
--- /dev/null
+++ b/src/core/hid/emulated_console.h
@@ -0,0 +1,190 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/point.h"
17#include "common/quaternion.h"
18#include "common/vector_math.h"
19#include "core/hid/hid_types.h"
20#include "core/hid/motion_input.h"
21
22namespace Core::HID {
23
24struct ConsoleMotionInfo {
25 Common::Input::MotionStatus raw_status{};
26 MotionInput emulated{};
27};
28
29using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
30using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
31
32using ConsoleMotionParams = Common::ParamPackage;
33using TouchParams = std::array<Common::ParamPackage, 16>;
34
35using ConsoleMotionValues = ConsoleMotionInfo;
36using TouchValues = std::array<Common::Input::TouchStatus, 16>;
37
38struct TouchFinger {
39 u64 last_touch{};
40 Common::Point<float> position{};
41 u32 id{};
42 TouchAttribute attribute{};
43 bool pressed{};
44};
45
46// Contains all motion related data that is used on the services
47struct ConsoleMotion {
48 Common::Vec3f accel{};
49 Common::Vec3f gyro{};
50 Common::Vec3f rotation{};
51 std::array<Common::Vec3f, 3> orientation{};
52 Common::Quaternion<f32> quaternion{};
53 bool is_at_rest{};
54};
55
56using TouchFingerState = std::array<TouchFinger, 16>;
57
58struct ConsoleStatus {
59 // Data from input_common
60 ConsoleMotionValues motion_values{};
61 TouchValues touch_values{};
62
63 // Data for HID services
64 ConsoleMotion motion_state{};
65 TouchFingerState touch_state{};
66};
67
68enum class ConsoleTriggerType {
69 Motion,
70 Touch,
71 All,
72};
73
74struct ConsoleUpdateCallback {
75 std::function<void(ConsoleTriggerType)> on_change;
76};
77
78class EmulatedConsole {
79public:
80 /**
81 * Contains all input data within the emulated switch console tablet such as touch and motion
82 */
83 explicit EmulatedConsole();
84 ~EmulatedConsole();
85
86 YUZU_NON_COPYABLE(EmulatedConsole);
87 YUZU_NON_MOVEABLE(EmulatedConsole);
88
89 /// Removes all callbacks created from input devices
90 void UnloadInput();
91
92 /**
93 * Sets the emulated console into configuring mode
94 * This prevents the modification of the HID state of the emulated console by input commands
95 */
96 void EnableConfiguration();
97
98 /// Returns the emulated console into normal mode, allowing the modification of the HID state
99 void DisableConfiguration();
100
101 /// Returns true if the emulated console is in configuring mode
102 bool IsConfiguring() const;
103
104 /// Reload all input devices
105 void ReloadInput();
106
107 /// Overrides current mapped devices with the stored configuration and reloads all input devices
108 void ReloadFromSettings();
109
110 /// Saves the current mapped configuration
111 void SaveCurrentConfig();
112
113 /// Reverts any mapped changes made that weren't saved
114 void RestoreConfig();
115
116 // Returns the current mapped motion device
117 Common::ParamPackage GetMotionParam() const;
118
119 /**
120 * Updates the current mapped motion device
121 * @param param ParamPackage with controller data to be mapped
122 */
123 void SetMotionParam(Common::ParamPackage param);
124
125 /// Returns the latest status of motion input from the console with parameters
126 ConsoleMotionValues GetMotionValues() const;
127
128 /// Returns the latest status of touch input from the console with parameters
129 TouchValues GetTouchValues() const;
130
131 /// Returns the latest status of motion input from the console
132 ConsoleMotion GetMotion() const;
133
134 /// Returns the latest status of touch input from the console
135 TouchFingerState GetTouch() const;
136
137 /**
138 * Adds a callback to the list of events
139 * @param update_callback A ConsoleUpdateCallback that will be triggered
140 * @return an unique key corresponding to the callback index in the list
141 */
142 int SetCallback(ConsoleUpdateCallback update_callback);
143
144 /**
145 * Removes a callback from the list stopping any future events to this object
146 * @param key Key corresponding to the callback index in the list
147 */
148 void DeleteCallback(int key);
149
150private:
151 /// Creates and stores the touch params
152 void SetTouchParams();
153
154 /**
155 * Updates the motion status of the console
156 * @param callback A CallbackStatus containing gyro and accelerometer data
157 */
158 void SetMotion(const Common::Input::CallbackStatus& callback);
159
160 /**
161 * Updates the touch status of the console
162 * @param callback A CallbackStatus containing the touch position
163 * @param index Finger ID to be updated
164 */
165 void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
166
167 /**
168 * Triggers a callback that something has changed on the console status
169 * @param type Input type of the event to trigger
170 */
171 void TriggerOnChange(ConsoleTriggerType type);
172
173 bool is_configuring{false};
174 f32 motion_sensitivity{0.01f};
175
176 ConsoleMotionParams motion_params;
177 TouchParams touch_params;
178
179 ConsoleMotionDevices motion_devices;
180 TouchDevices touch_devices;
181
182 mutable std::mutex mutex;
183 std::unordered_map<int, ConsoleUpdateCallback> callback_list;
184 int last_callback_key = 0;
185
186 // Stores the current status of all console input
187 ConsoleStatus console;
188};
189
190} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
new file mode 100644
index 000000000..93372445b
--- /dev/null
+++ b/src/core/hid/emulated_controller.cpp
@@ -0,0 +1,1139 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/input_converter.h"
7
8namespace Core::HID {
9constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
10constexpr s32 HID_TRIGGER_MAX = 0x7fff;
11
12EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
13
14EmulatedController::~EmulatedController() = default;
15
16NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
17 switch (type) {
18 case Settings::ControllerType::ProController:
19 return NpadStyleIndex::ProController;
20 case Settings::ControllerType::DualJoyconDetached:
21 return NpadStyleIndex::JoyconDual;
22 case Settings::ControllerType::LeftJoycon:
23 return NpadStyleIndex::JoyconLeft;
24 case Settings::ControllerType::RightJoycon:
25 return NpadStyleIndex::JoyconRight;
26 case Settings::ControllerType::Handheld:
27 return NpadStyleIndex::Handheld;
28 case Settings::ControllerType::GameCube:
29 return NpadStyleIndex::GameCube;
30 case Settings::ControllerType::Pokeball:
31 return NpadStyleIndex::Pokeball;
32 case Settings::ControllerType::NES:
33 return NpadStyleIndex::NES;
34 case Settings::ControllerType::SNES:
35 return NpadStyleIndex::SNES;
36 case Settings::ControllerType::N64:
37 return NpadStyleIndex::N64;
38 case Settings::ControllerType::SegaGenesis:
39 return NpadStyleIndex::SegaGenesis;
40 default:
41 return NpadStyleIndex::ProController;
42 }
43}
44
45Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
46 switch (type) {
47 case NpadStyleIndex::ProController:
48 return Settings::ControllerType::ProController;
49 case NpadStyleIndex::JoyconDual:
50 return Settings::ControllerType::DualJoyconDetached;
51 case NpadStyleIndex::JoyconLeft:
52 return Settings::ControllerType::LeftJoycon;
53 case NpadStyleIndex::JoyconRight:
54 return Settings::ControllerType::RightJoycon;
55 case NpadStyleIndex::Handheld:
56 return Settings::ControllerType::Handheld;
57 case NpadStyleIndex::GameCube:
58 return Settings::ControllerType::GameCube;
59 case NpadStyleIndex::Pokeball:
60 return Settings::ControllerType::Pokeball;
61 case NpadStyleIndex::NES:
62 return Settings::ControllerType::NES;
63 case NpadStyleIndex::SNES:
64 return Settings::ControllerType::SNES;
65 case NpadStyleIndex::N64:
66 return Settings::ControllerType::N64;
67 case NpadStyleIndex::SegaGenesis:
68 return Settings::ControllerType::SegaGenesis;
69 default:
70 return Settings::ControllerType::ProController;
71 }
72}
73
74void EmulatedController::ReloadFromSettings() {
75 const auto player_index = NpadIdTypeToIndex(npad_id_type);
76 const auto& player = Settings::values.players.GetValue()[player_index];
77
78 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
79 button_params[index] = Common::ParamPackage(player.buttons[index]);
80 }
81 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
82 stick_params[index] = Common::ParamPackage(player.analogs[index]);
83 }
84 for (std::size_t index = 0; index < player.motions.size(); ++index) {
85 motion_params[index] = Common::ParamPackage(player.motions[index]);
86 }
87
88 controller.colors_state.left = {
89 .body = player.body_color_left,
90 .button = player.button_color_left,
91 };
92
93 controller.colors_state.right = {
94 .body = player.body_color_right,
95 .button = player.button_color_right,
96 };
97
98 controller.colors_state.fullkey = controller.colors_state.left;
99
100 // Other or debug controller should always be a pro controller
101 if (npad_id_type != NpadIdType::Other) {
102 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
103 } else {
104 SetNpadStyleIndex(NpadStyleIndex::ProController);
105 }
106
107 if (player.connected) {
108 Connect();
109 } else {
110 Disconnect();
111 }
112
113 ReloadInput();
114}
115
116void EmulatedController::LoadDevices() {
117 // TODO(german77): Use more buttons to detect the correct device
118 const auto left_joycon = button_params[Settings::NativeButton::DRight];
119 const auto right_joycon = button_params[Settings::NativeButton::A];
120
121 // Triggers for GC controllers
122 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
123 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
124
125 battery_params[LeftIndex] = left_joycon;
126 battery_params[RightIndex] = right_joycon;
127 battery_params[LeftIndex].Set("battery", true);
128 battery_params[RightIndex].Set("battery", true);
129
130 output_params[LeftIndex] = left_joycon;
131 output_params[RightIndex] = right_joycon;
132 output_params[LeftIndex].Set("output", true);
133 output_params[RightIndex].Set("output", true);
134
135 LoadTASParams();
136
137 std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
138 button_params.begin() + Settings::NativeButton::BUTTON_NS_END,
139 button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
140 std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
141 stick_params.begin() + Settings::NativeAnalog::STICK_HID_END,
142 stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
143 std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
144 motion_params.begin() + Settings::NativeMotion::MOTION_HID_END,
145 motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
146 std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
147 Common::Input::CreateDevice<Common::Input::InputDevice>);
148 std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(),
149 Common::Input::CreateDevice<Common::Input::InputDevice>);
150 std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
151 Common::Input::CreateDevice<Common::Input::OutputDevice>);
152
153 // Initialize TAS devices
154 std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(),
155 Common::Input::CreateDevice<Common::Input::InputDevice>);
156 std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(),
157 Common::Input::CreateDevice<Common::Input::InputDevice>);
158}
159
160void EmulatedController::LoadTASParams() {
161 const auto player_index = NpadIdTypeToIndex(npad_id_type);
162 Common::ParamPackage common_params{};
163 common_params.Set("engine", "tas");
164 common_params.Set("port", static_cast<int>(player_index));
165 for (auto& param : tas_button_params) {
166 param = common_params;
167 }
168 for (auto& param : tas_stick_params) {
169 param = common_params;
170 }
171
172 // TODO(german77): Replace this with an input profile or something better
173 tas_button_params[Settings::NativeButton::A].Set("button", 0);
174 tas_button_params[Settings::NativeButton::B].Set("button", 1);
175 tas_button_params[Settings::NativeButton::X].Set("button", 2);
176 tas_button_params[Settings::NativeButton::Y].Set("button", 3);
177 tas_button_params[Settings::NativeButton::LStick].Set("button", 4);
178 tas_button_params[Settings::NativeButton::RStick].Set("button", 5);
179 tas_button_params[Settings::NativeButton::L].Set("button", 6);
180 tas_button_params[Settings::NativeButton::R].Set("button", 7);
181 tas_button_params[Settings::NativeButton::ZL].Set("button", 8);
182 tas_button_params[Settings::NativeButton::ZR].Set("button", 9);
183 tas_button_params[Settings::NativeButton::Plus].Set("button", 10);
184 tas_button_params[Settings::NativeButton::Minus].Set("button", 11);
185 tas_button_params[Settings::NativeButton::DLeft].Set("button", 12);
186 tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
187 tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
188 tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
189 tas_button_params[Settings::NativeButton::SL].Set("button", 16);
190 tas_button_params[Settings::NativeButton::SR].Set("button", 17);
191 tas_button_params[Settings::NativeButton::Home].Set("button", 18);
192 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
193
194 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
195 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
196 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
197 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
198}
199
200void EmulatedController::ReloadInput() {
201 // If you load any device here add the equivalent to the UnloadInput() function
202 LoadDevices();
203 for (std::size_t index = 0; index < button_devices.size(); ++index) {
204 if (!button_devices[index]) {
205 continue;
206 }
207 const auto uuid = Common::UUID{button_params[index].Get("guid", "")};
208 button_devices[index]->SetCallback({
209 .on_change =
210 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
211 SetButton(callback, index, uuid);
212 },
213 });
214 button_devices[index]->ForceUpdate();
215 }
216
217 for (std::size_t index = 0; index < stick_devices.size(); ++index) {
218 if (!stick_devices[index]) {
219 continue;
220 }
221 const auto uuid = Common::UUID{stick_params[index].Get("guid", "")};
222 stick_devices[index]->SetCallback({
223 .on_change =
224 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
225 SetStick(callback, index, uuid);
226 },
227 });
228 stick_devices[index]->ForceUpdate();
229 }
230
231 for (std::size_t index = 0; index < trigger_devices.size(); ++index) {
232 if (!trigger_devices[index]) {
233 continue;
234 }
235 const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")};
236 trigger_devices[index]->SetCallback({
237 .on_change =
238 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
239 SetTrigger(callback, index, uuid);
240 },
241 });
242 trigger_devices[index]->ForceUpdate();
243 }
244
245 for (std::size_t index = 0; index < battery_devices.size(); ++index) {
246 if (!battery_devices[index]) {
247 continue;
248 }
249 battery_devices[index]->SetCallback({
250 .on_change =
251 [this, index](const Common::Input::CallbackStatus& callback) {
252 SetBattery(callback, index);
253 },
254 });
255 battery_devices[index]->ForceUpdate();
256 }
257
258 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
259 if (!motion_devices[index]) {
260 continue;
261 }
262 motion_devices[index]->SetCallback({
263 .on_change =
264 [this, index](const Common::Input::CallbackStatus& callback) {
265 SetMotion(callback, index);
266 },
267 });
268 motion_devices[index]->ForceUpdate();
269 }
270
271 // Use a common UUID for TAS
272 const auto tas_uuid = Common::UUID{0x0, 0x7A5};
273
274 // Register TAS devices. No need to force update
275 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
276 if (!tas_button_devices[index]) {
277 continue;
278 }
279 tas_button_devices[index]->SetCallback({
280 .on_change =
281 [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
282 SetButton(callback, index, tas_uuid);
283 },
284 });
285 }
286
287 for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
288 if (!tas_stick_devices[index]) {
289 continue;
290 }
291 tas_stick_devices[index]->SetCallback({
292 .on_change =
293 [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
294 SetStick(callback, index, tas_uuid);
295 },
296 });
297 }
298}
299
300void EmulatedController::UnloadInput() {
301 for (auto& button : button_devices) {
302 button.reset();
303 }
304 for (auto& stick : stick_devices) {
305 stick.reset();
306 }
307 for (auto& motion : motion_devices) {
308 motion.reset();
309 }
310 for (auto& trigger : trigger_devices) {
311 trigger.reset();
312 }
313 for (auto& battery : battery_devices) {
314 battery.reset();
315 }
316 for (auto& output : output_devices) {
317 output.reset();
318 }
319 for (auto& button : tas_button_devices) {
320 button.reset();
321 }
322 for (auto& stick : tas_stick_devices) {
323 stick.reset();
324 }
325}
326
327void EmulatedController::EnableConfiguration() {
328 is_configuring = true;
329 tmp_is_connected = is_connected;
330 tmp_npad_type = npad_type;
331}
332
333void EmulatedController::DisableConfiguration() {
334 is_configuring = false;
335
336 // Apply temporary npad type to the real controller
337 if (tmp_npad_type != npad_type) {
338 if (is_connected) {
339 Disconnect();
340 }
341 SetNpadStyleIndex(tmp_npad_type);
342 }
343
344 // Apply temporary connected status to the real controller
345 if (tmp_is_connected != is_connected) {
346 if (tmp_is_connected) {
347 Connect();
348 return;
349 }
350 Disconnect();
351 }
352}
353
354bool EmulatedController::IsConfiguring() const {
355 return is_configuring;
356}
357
358void EmulatedController::SaveCurrentConfig() {
359 const auto player_index = NpadIdTypeToIndex(npad_id_type);
360 auto& player = Settings::values.players.GetValue()[player_index];
361 player.connected = is_connected;
362 player.controller_type = MapNPadToSettingsType(npad_type);
363 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
364 player.buttons[index] = button_params[index].Serialize();
365 }
366 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
367 player.analogs[index] = stick_params[index].Serialize();
368 }
369 for (std::size_t index = 0; index < player.motions.size(); ++index) {
370 player.motions[index] = motion_params[index].Serialize();
371 }
372}
373
374void EmulatedController::RestoreConfig() {
375 if (!is_configuring) {
376 return;
377 }
378 ReloadFromSettings();
379}
380
381std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
382 EmulatedDeviceIndex device_index) const {
383 std::vector<Common::ParamPackage> devices;
384 for (const auto& param : button_params) {
385 if (!param.Has("engine")) {
386 continue;
387 }
388 const auto devices_it = std::find_if(
389 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
390 return param.Get("engine", "") == param_.Get("engine", "") &&
391 param.Get("guid", "") == param_.Get("guid", "") &&
392 param.Get("port", 0) == param_.Get("port", 0);
393 });
394 if (devices_it != devices.end()) {
395 continue;
396 }
397 Common::ParamPackage device{};
398 device.Set("engine", param.Get("engine", ""));
399 device.Set("guid", param.Get("guid", ""));
400 device.Set("port", param.Get("port", 0));
401 devices.push_back(device);
402 }
403
404 for (const auto& param : stick_params) {
405 if (!param.Has("engine")) {
406 continue;
407 }
408 if (param.Get("engine", "") == "analog_from_button") {
409 continue;
410 }
411 const auto devices_it = std::find_if(
412 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
413 return param.Get("engine", "") == param_.Get("engine", "") &&
414 param.Get("guid", "") == param_.Get("guid", "") &&
415 param.Get("port", 0) == param_.Get("port", 0);
416 });
417 if (devices_it != devices.end()) {
418 continue;
419 }
420 Common::ParamPackage device{};
421 device.Set("engine", param.Get("engine", ""));
422 device.Set("guid", param.Get("guid", ""));
423 device.Set("port", param.Get("port", 0));
424 devices.push_back(device);
425 }
426 return devices;
427}
428
429Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const {
430 if (index >= button_params.size()) {
431 return {};
432 }
433 return button_params[index];
434}
435
436Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const {
437 if (index >= stick_params.size()) {
438 return {};
439 }
440 return stick_params[index];
441}
442
443Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const {
444 if (index >= motion_params.size()) {
445 return {};
446 }
447 return motion_params[index];
448}
449
450void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) {
451 if (index >= button_params.size()) {
452 return;
453 }
454 button_params[index] = std::move(param);
455 ReloadInput();
456}
457
458void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) {
459 if (index >= stick_params.size()) {
460 return;
461 }
462 stick_params[index] = std::move(param);
463 ReloadInput();
464}
465
466void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
467 if (index >= motion_params.size()) {
468 return;
469 }
470 motion_params[index] = std::move(param);
471 ReloadInput();
472}
473
474void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
475 Common::UUID uuid) {
476 if (index >= controller.button_values.size()) {
477 return;
478 }
479 {
480 std::lock_guard lock{mutex};
481 bool value_changed = false;
482 const auto new_status = TransformToButton(callback);
483 auto& current_status = controller.button_values[index];
484
485 // Only read button values that have the same uuid or are pressed once
486 if (current_status.uuid != uuid) {
487 if (!new_status.value) {
488 return;
489 }
490 }
491
492 current_status.toggle = new_status.toggle;
493 current_status.uuid = uuid;
494
495 // Update button status with current
496 if (!current_status.toggle) {
497 current_status.locked = false;
498 if (current_status.value != new_status.value) {
499 current_status.value = new_status.value;
500 value_changed = true;
501 }
502 } else {
503 // Toggle button and lock status
504 if (new_status.value && !current_status.locked) {
505 current_status.locked = true;
506 current_status.value = !current_status.value;
507 value_changed = true;
508 }
509
510 // Unlock button ready for next press
511 if (!new_status.value && current_status.locked) {
512 current_status.locked = false;
513 }
514 }
515
516 if (!value_changed) {
517 return;
518 }
519
520 if (is_configuring) {
521 controller.npad_button_state.raw = NpadButton::None;
522 controller.debug_pad_button_state.raw = 0;
523 TriggerOnChange(ControllerTriggerType::Button, false);
524 return;
525 }
526
527 switch (index) {
528 case Settings::NativeButton::A:
529 controller.npad_button_state.a.Assign(current_status.value);
530 controller.debug_pad_button_state.a.Assign(current_status.value);
531 break;
532 case Settings::NativeButton::B:
533 controller.npad_button_state.b.Assign(current_status.value);
534 controller.debug_pad_button_state.b.Assign(current_status.value);
535 break;
536 case Settings::NativeButton::X:
537 controller.npad_button_state.x.Assign(current_status.value);
538 controller.debug_pad_button_state.x.Assign(current_status.value);
539 break;
540 case Settings::NativeButton::Y:
541 controller.npad_button_state.y.Assign(current_status.value);
542 controller.debug_pad_button_state.y.Assign(current_status.value);
543 break;
544 case Settings::NativeButton::LStick:
545 controller.npad_button_state.stick_l.Assign(current_status.value);
546 break;
547 case Settings::NativeButton::RStick:
548 controller.npad_button_state.stick_r.Assign(current_status.value);
549 break;
550 case Settings::NativeButton::L:
551 controller.npad_button_state.l.Assign(current_status.value);
552 controller.debug_pad_button_state.l.Assign(current_status.value);
553 break;
554 case Settings::NativeButton::R:
555 controller.npad_button_state.r.Assign(current_status.value);
556 controller.debug_pad_button_state.r.Assign(current_status.value);
557 break;
558 case Settings::NativeButton::ZL:
559 controller.npad_button_state.zl.Assign(current_status.value);
560 controller.debug_pad_button_state.zl.Assign(current_status.value);
561 break;
562 case Settings::NativeButton::ZR:
563 controller.npad_button_state.zr.Assign(current_status.value);
564 controller.debug_pad_button_state.zr.Assign(current_status.value);
565 break;
566 case Settings::NativeButton::Plus:
567 controller.npad_button_state.plus.Assign(current_status.value);
568 controller.debug_pad_button_state.plus.Assign(current_status.value);
569 break;
570 case Settings::NativeButton::Minus:
571 controller.npad_button_state.minus.Assign(current_status.value);
572 controller.debug_pad_button_state.minus.Assign(current_status.value);
573 break;
574 case Settings::NativeButton::DLeft:
575 controller.npad_button_state.left.Assign(current_status.value);
576 controller.debug_pad_button_state.d_left.Assign(current_status.value);
577 break;
578 case Settings::NativeButton::DUp:
579 controller.npad_button_state.up.Assign(current_status.value);
580 controller.debug_pad_button_state.d_up.Assign(current_status.value);
581 break;
582 case Settings::NativeButton::DRight:
583 controller.npad_button_state.right.Assign(current_status.value);
584 controller.debug_pad_button_state.d_right.Assign(current_status.value);
585 break;
586 case Settings::NativeButton::DDown:
587 controller.npad_button_state.down.Assign(current_status.value);
588 controller.debug_pad_button_state.d_down.Assign(current_status.value);
589 break;
590 case Settings::NativeButton::SL:
591 controller.npad_button_state.left_sl.Assign(current_status.value);
592 controller.npad_button_state.right_sl.Assign(current_status.value);
593 break;
594 case Settings::NativeButton::SR:
595 controller.npad_button_state.left_sr.Assign(current_status.value);
596 controller.npad_button_state.right_sr.Assign(current_status.value);
597 break;
598 case Settings::NativeButton::Home:
599 case Settings::NativeButton::Screenshot:
600 break;
601 }
602 }
603 if (!is_connected) {
604 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
605 Connect();
606 }
607 if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) {
608 Connect();
609 }
610 }
611 TriggerOnChange(ControllerTriggerType::Button, true);
612}
613
614void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
615 Common::UUID uuid) {
616 if (index >= controller.stick_values.size()) {
617 return;
618 }
619 std::lock_guard lock{mutex};
620 const auto stick_value = TransformToStick(callback);
621
622 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
623 if (controller.stick_values[index].uuid != uuid) {
624 if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) {
625 return;
626 }
627 }
628
629 controller.stick_values[index] = stick_value;
630 controller.stick_values[index].uuid = uuid;
631
632 if (is_configuring) {
633 controller.analog_stick_state.left = {};
634 controller.analog_stick_state.right = {};
635 TriggerOnChange(ControllerTriggerType::Stick, false);
636 return;
637 }
638
639 const AnalogStickState stick{
640 .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX),
641 .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX),
642 };
643
644 switch (index) {
645 case Settings::NativeAnalog::LStick:
646 controller.analog_stick_state.left = stick;
647 controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left);
648 controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up);
649 controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right);
650 controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down);
651 break;
652 case Settings::NativeAnalog::RStick:
653 controller.analog_stick_state.right = stick;
654 controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left);
655 controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up);
656 controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right);
657 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
658 break;
659 }
660
661 TriggerOnChange(ControllerTriggerType::Stick, true);
662}
663
664void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
665 std::size_t index, Common::UUID uuid) {
666 if (index >= controller.trigger_values.size()) {
667 return;
668 }
669 std::lock_guard lock{mutex};
670 const auto trigger_value = TransformToTrigger(callback);
671
672 // Only read trigger values that have the same uuid or are pressed once
673 if (controller.trigger_values[index].uuid != uuid) {
674 if (!trigger_value.pressed.value) {
675 return;
676 }
677 }
678
679 controller.trigger_values[index] = trigger_value;
680 controller.trigger_values[index].uuid = uuid;
681
682 if (is_configuring) {
683 controller.gc_trigger_state.left = 0;
684 controller.gc_trigger_state.right = 0;
685 TriggerOnChange(ControllerTriggerType::Trigger, false);
686 return;
687 }
688
689 const auto& trigger = controller.trigger_values[index];
690
691 switch (index) {
692 case Settings::NativeTrigger::LTrigger:
693 controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
694 controller.npad_button_state.zl.Assign(trigger.pressed.value);
695 break;
696 case Settings::NativeTrigger::RTrigger:
697 controller.gc_trigger_state.right =
698 static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
699 controller.npad_button_state.zr.Assign(trigger.pressed.value);
700 break;
701 }
702
703 TriggerOnChange(ControllerTriggerType::Trigger, true);
704}
705
706void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
707 std::size_t index) {
708 if (index >= controller.motion_values.size()) {
709 return;
710 }
711 std::lock_guard lock{mutex};
712 auto& raw_status = controller.motion_values[index].raw_status;
713 auto& emulated = controller.motion_values[index].emulated;
714
715 raw_status = TransformToMotion(callback);
716 emulated.SetAcceleration(Common::Vec3f{
717 raw_status.accel.x.value,
718 raw_status.accel.y.value,
719 raw_status.accel.z.value,
720 });
721 emulated.SetGyroscope(Common::Vec3f{
722 raw_status.gyro.x.value,
723 raw_status.gyro.y.value,
724 raw_status.gyro.z.value,
725 });
726 emulated.UpdateRotation(raw_status.delta_timestamp);
727 emulated.UpdateOrientation(raw_status.delta_timestamp);
728 force_update_motion = raw_status.force_update;
729
730 if (is_configuring) {
731 TriggerOnChange(ControllerTriggerType::Motion, false);
732 return;
733 }
734
735 auto& motion = controller.motion_state[index];
736 motion.accel = emulated.GetAcceleration();
737 motion.gyro = emulated.GetGyroscope();
738 motion.rotation = emulated.GetRotations();
739 motion.orientation = emulated.GetOrientation();
740 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
741
742 TriggerOnChange(ControllerTriggerType::Motion, true);
743}
744
745void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
746 std::size_t index) {
747 if (index >= controller.battery_values.size()) {
748 return;
749 }
750 std::lock_guard lock{mutex};
751 controller.battery_values[index] = TransformToBattery(callback);
752
753 if (is_configuring) {
754 TriggerOnChange(ControllerTriggerType::Battery, false);
755 return;
756 }
757
758 bool is_charging = false;
759 bool is_powered = false;
760 NpadBatteryLevel battery_level = 0;
761 switch (controller.battery_values[index]) {
762 case Common::Input::BatteryLevel::Charging:
763 is_charging = true;
764 is_powered = true;
765 battery_level = 6;
766 break;
767 case Common::Input::BatteryLevel::Medium:
768 battery_level = 6;
769 break;
770 case Common::Input::BatteryLevel::Low:
771 battery_level = 4;
772 break;
773 case Common::Input::BatteryLevel::Critical:
774 battery_level = 2;
775 break;
776 case Common::Input::BatteryLevel::Empty:
777 battery_level = 0;
778 break;
779 case Common::Input::BatteryLevel::None:
780 case Common::Input::BatteryLevel::Full:
781 default:
782 is_powered = true;
783 battery_level = 8;
784 break;
785 }
786
787 switch (index) {
788 case LeftIndex:
789 controller.battery_state.left = {
790 .is_powered = is_powered,
791 .is_charging = is_charging,
792 .battery_level = battery_level,
793 };
794 break;
795 case RightIndex:
796 controller.battery_state.right = {
797 .is_powered = is_powered,
798 .is_charging = is_charging,
799 .battery_level = battery_level,
800 };
801 break;
802 case DualIndex:
803 controller.battery_state.dual = {
804 .is_powered = is_powered,
805 .is_charging = is_charging,
806 .battery_level = battery_level,
807 };
808 break;
809 }
810 TriggerOnChange(ControllerTriggerType::Battery, true);
811}
812
813bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
814 if (device_index >= output_devices.size()) {
815 return false;
816 }
817 if (!output_devices[device_index]) {
818 return false;
819 }
820 const auto player_index = NpadIdTypeToIndex(npad_id_type);
821 const auto& player = Settings::values.players.GetValue()[player_index];
822 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
823
824 if (!player.vibration_enabled) {
825 return false;
826 }
827
828 // Exponential amplification is too strong at low amplitudes. Switch to a linear
829 // amplification if strength is set below 0.7f
830 const Common::Input::VibrationAmplificationType type =
831 strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential
832 : Common::Input::VibrationAmplificationType::Linear;
833
834 const Common::Input::VibrationStatus status = {
835 .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f),
836 .low_frequency = vibration.low_frequency,
837 .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f),
838 .high_frequency = vibration.high_frequency,
839 .type = type,
840 };
841 return output_devices[device_index]->SetVibration(status) ==
842 Common::Input::VibrationError::None;
843}
844
845bool EmulatedController::TestVibration(std::size_t device_index) {
846 if (device_index >= output_devices.size()) {
847 return false;
848 }
849 if (!output_devices[device_index]) {
850 return false;
851 }
852
853 // Send a slight vibration to test for rumble support
854 constexpr Common::Input::VibrationStatus status = {
855 .low_amplitude = 0.001f,
856 .low_frequency = 160.0f,
857 .high_amplitude = 0.001f,
858 .high_frequency = 320.0f,
859 .type = Common::Input::VibrationAmplificationType::Linear,
860 };
861 return output_devices[device_index]->SetVibration(status) ==
862 Common::Input::VibrationError::None;
863}
864
865void EmulatedController::SetLedPattern() {
866 for (auto& device : output_devices) {
867 if (!device) {
868 continue;
869 }
870
871 const LedPattern pattern = GetLedPattern();
872 const Common::Input::LedStatus status = {
873 .led_1 = pattern.position1 != 0,
874 .led_2 = pattern.position2 != 0,
875 .led_3 = pattern.position3 != 0,
876 .led_4 = pattern.position4 != 0,
877 };
878 device->SetLED(status);
879 }
880}
881
882void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
883 supported_style_tag = supported_styles;
884 if (!is_connected) {
885 return;
886 }
887 if (!IsControllerSupported()) {
888 LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
889 npad_type);
890 Disconnect();
891 }
892}
893
894bool EmulatedController::IsControllerSupported() const {
895 switch (npad_type) {
896 case NpadStyleIndex::ProController:
897 return supported_style_tag.fullkey;
898 case NpadStyleIndex::Handheld:
899 return supported_style_tag.handheld;
900 case NpadStyleIndex::JoyconDual:
901 return supported_style_tag.joycon_dual;
902 case NpadStyleIndex::JoyconLeft:
903 return supported_style_tag.joycon_left;
904 case NpadStyleIndex::JoyconRight:
905 return supported_style_tag.joycon_right;
906 case NpadStyleIndex::GameCube:
907 return supported_style_tag.gamecube;
908 case NpadStyleIndex::Pokeball:
909 return supported_style_tag.palma;
910 case NpadStyleIndex::NES:
911 return supported_style_tag.lark;
912 case NpadStyleIndex::SNES:
913 return supported_style_tag.lucia;
914 case NpadStyleIndex::N64:
915 return supported_style_tag.lagoon;
916 case NpadStyleIndex::SegaGenesis:
917 return supported_style_tag.lager;
918 default:
919 return false;
920 }
921}
922
923void EmulatedController::Connect() {
924 if (!IsControllerSupported()) {
925 LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
926 return;
927 }
928 {
929 std::lock_guard lock{mutex};
930 if (is_configuring) {
931 tmp_is_connected = true;
932 TriggerOnChange(ControllerTriggerType::Connected, false);
933 return;
934 }
935
936 if (is_connected) {
937 return;
938 }
939 is_connected = true;
940 }
941 TriggerOnChange(ControllerTriggerType::Connected, true);
942}
943
944void EmulatedController::Disconnect() {
945 {
946 std::lock_guard lock{mutex};
947 if (is_configuring) {
948 tmp_is_connected = false;
949 TriggerOnChange(ControllerTriggerType::Disconnected, false);
950 return;
951 }
952
953 if (!is_connected) {
954 return;
955 }
956 is_connected = false;
957 }
958 TriggerOnChange(ControllerTriggerType::Disconnected, true);
959}
960
961bool EmulatedController::IsConnected(bool get_temporary_value) const {
962 if (get_temporary_value && is_configuring) {
963 return tmp_is_connected;
964 }
965 return is_connected;
966}
967
968bool EmulatedController::IsVibrationEnabled() const {
969 const auto player_index = NpadIdTypeToIndex(npad_id_type);
970 const auto& player = Settings::values.players.GetValue()[player_index];
971 return player.vibration_enabled;
972}
973
974NpadIdType EmulatedController::GetNpadIdType() const {
975 return npad_id_type;
976}
977
978NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
979 if (get_temporary_value && is_configuring) {
980 return tmp_npad_type;
981 }
982 return npad_type;
983}
984
985void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
986 {
987 std::lock_guard lock{mutex};
988
989 if (is_configuring) {
990 if (tmp_npad_type == npad_type_) {
991 return;
992 }
993 tmp_npad_type = npad_type_;
994 TriggerOnChange(ControllerTriggerType::Type, false);
995 return;
996 }
997
998 if (npad_type == npad_type_) {
999 return;
1000 }
1001 if (is_connected) {
1002 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1003 NpadIdTypeToIndex(npad_id_type));
1004 }
1005 npad_type = npad_type_;
1006 }
1007 TriggerOnChange(ControllerTriggerType::Type, true);
1008}
1009
1010LedPattern EmulatedController::GetLedPattern() const {
1011 switch (npad_id_type) {
1012 case NpadIdType::Player1:
1013 return LedPattern{1, 0, 0, 0};
1014 case NpadIdType::Player2:
1015 return LedPattern{1, 1, 0, 0};
1016 case NpadIdType::Player3:
1017 return LedPattern{1, 1, 1, 0};
1018 case NpadIdType::Player4:
1019 return LedPattern{1, 1, 1, 1};
1020 case NpadIdType::Player5:
1021 return LedPattern{1, 0, 0, 1};
1022 case NpadIdType::Player6:
1023 return LedPattern{1, 0, 1, 0};
1024 case NpadIdType::Player7:
1025 return LedPattern{1, 0, 1, 1};
1026 case NpadIdType::Player8:
1027 return LedPattern{0, 1, 1, 0};
1028 default:
1029 return LedPattern{0, 0, 0, 0};
1030 }
1031}
1032
1033ButtonValues EmulatedController::GetButtonsValues() const {
1034 return controller.button_values;
1035}
1036
1037SticksValues EmulatedController::GetSticksValues() const {
1038 return controller.stick_values;
1039}
1040
1041TriggerValues EmulatedController::GetTriggersValues() const {
1042 return controller.trigger_values;
1043}
1044
1045ControllerMotionValues EmulatedController::GetMotionValues() const {
1046 return controller.motion_values;
1047}
1048
1049ColorValues EmulatedController::GetColorsValues() const {
1050 return controller.color_values;
1051}
1052
1053BatteryValues EmulatedController::GetBatteryValues() const {
1054 return controller.battery_values;
1055}
1056
1057NpadButtonState EmulatedController::GetNpadButtons() const {
1058 if (is_configuring) {
1059 return {};
1060 }
1061 return controller.npad_button_state;
1062}
1063
1064DebugPadButton EmulatedController::GetDebugPadButtons() const {
1065 if (is_configuring) {
1066 return {};
1067 }
1068 return controller.debug_pad_button_state;
1069}
1070
1071AnalogSticks EmulatedController::GetSticks() const {
1072 if (is_configuring) {
1073 return {};
1074 }
1075 // Some drivers like stick from buttons need constant refreshing
1076 for (auto& device : stick_devices) {
1077 if (!device) {
1078 continue;
1079 }
1080 device->SoftUpdate();
1081 }
1082 return controller.analog_stick_state;
1083}
1084
1085NpadGcTriggerState EmulatedController::GetTriggers() const {
1086 if (is_configuring) {
1087 return {};
1088 }
1089 return controller.gc_trigger_state;
1090}
1091
1092MotionState EmulatedController::GetMotions() const {
1093 if (force_update_motion) {
1094 for (auto& device : motion_devices) {
1095 if (!device) {
1096 continue;
1097 }
1098 device->ForceUpdate();
1099 }
1100 }
1101 return controller.motion_state;
1102}
1103
1104ControllerColors EmulatedController::GetColors() const {
1105 return controller.colors_state;
1106}
1107
1108BatteryLevelState EmulatedController::GetBattery() const {
1109 return controller.battery_state;
1110}
1111
1112void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1113 for (const auto& poller_pair : callback_list) {
1114 const ControllerUpdateCallback& poller = poller_pair.second;
1115 if (!is_npad_service_update && poller.is_npad_service) {
1116 continue;
1117 }
1118 if (poller.on_change) {
1119 poller.on_change(type);
1120 }
1121 }
1122}
1123
1124int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
1125 std::lock_guard lock{mutex};
1126 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
1127 return last_callback_key++;
1128}
1129
1130void EmulatedController::DeleteCallback(int key) {
1131 std::lock_guard lock{mutex};
1132 const auto& iterator = callback_list.find(key);
1133 if (iterator == callback_list.end()) {
1134 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
1135 return;
1136 }
1137 callback_list.erase(iterator);
1138}
1139} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
new file mode 100644
index 000000000..e42aafebc
--- /dev/null
+++ b/src/core/hid/emulated_controller.h
@@ -0,0 +1,411 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/point.h"
17#include "common/quaternion.h"
18#include "common/settings.h"
19#include "common/vector_math.h"
20#include "core/hid/hid_types.h"
21#include "core/hid/motion_input.h"
22
23namespace Core::HID {
24const std::size_t max_emulated_controllers = 2;
25struct ControllerMotionInfo {
26 Common::Input::MotionStatus raw_status{};
27 MotionInput emulated{};
28};
29
30using ButtonDevices =
31 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>;
32using StickDevices =
33 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
34using ControllerMotionDevices =
35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
36using TriggerDevices =
37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
38using BatteryDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
40using OutputDevices =
41 std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
42
43using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
44using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
45using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
46using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
47using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
48using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
49
50using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
51using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
52using TriggerValues =
53 std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
54using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
55using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
56using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
57using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
58
59struct AnalogSticks {
60 AnalogStickState left{};
61 AnalogStickState right{};
62};
63
64struct ControllerColors {
65 NpadControllerColor fullkey{};
66 NpadControllerColor left{};
67 NpadControllerColor right{};
68};
69
70struct BatteryLevelState {
71 NpadPowerInfo dual{};
72 NpadPowerInfo left{};
73 NpadPowerInfo right{};
74};
75
76struct ControllerMotion {
77 Common::Vec3f accel{};
78 Common::Vec3f gyro{};
79 Common::Vec3f rotation{};
80 std::array<Common::Vec3f, 3> orientation{};
81 bool is_at_rest{};
82};
83
84enum EmulatedDeviceIndex : u8 {
85 LeftIndex,
86 RightIndex,
87 DualIndex,
88 AllDevices,
89};
90
91using MotionState = std::array<ControllerMotion, 2>;
92
93struct ControllerStatus {
94 // Data from input_common
95 ButtonValues button_values{};
96 SticksValues stick_values{};
97 ControllerMotionValues motion_values{};
98 TriggerValues trigger_values{};
99 ColorValues color_values{};
100 BatteryValues battery_values{};
101 VibrationValues vibration_values{};
102
103 // Data for HID serices
104 NpadButtonState npad_button_state{};
105 DebugPadButton debug_pad_button_state{};
106 AnalogSticks analog_stick_state{};
107 MotionState motion_state{};
108 NpadGcTriggerState gc_trigger_state{};
109 ControllerColors colors_state{};
110 BatteryLevelState battery_state{};
111};
112
113enum class ControllerTriggerType {
114 Button,
115 Stick,
116 Trigger,
117 Motion,
118 Color,
119 Battery,
120 Vibration,
121 Connected,
122 Disconnected,
123 Type,
124 All,
125};
126
127struct ControllerUpdateCallback {
128 std::function<void(ControllerTriggerType)> on_change;
129 bool is_npad_service;
130};
131
132class EmulatedController {
133public:
134 /**
135 * Contains all input data (buttons, joysticks, vibration, and motion) within this controller.
136 * @param npad_id_type npad id type for this specific controller
137 */
138 explicit EmulatedController(NpadIdType npad_id_type_);
139 ~EmulatedController();
140
141 YUZU_NON_COPYABLE(EmulatedController);
142 YUZU_NON_MOVEABLE(EmulatedController);
143
144 /// Converts the controller type from settings to npad type
145 static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type);
146
147 /// Converts npad type to the equivalent of controller type from settings
148 static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type);
149
150 /// Gets the NpadIdType for this controller
151 NpadIdType GetNpadIdType() const;
152
153 /// Sets the NpadStyleIndex for this controller
154 void SetNpadStyleIndex(NpadStyleIndex npad_type_);
155
156 /**
157 * Gets the NpadStyleIndex for this controller
158 * @param get_temporary_value If true tmp_npad_type will be returned
159 * @return NpadStyleIndex set on the controller
160 */
161 NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
162
163 /**
164 * Sets the supported controller types. Disconnects the controller if current type is not
165 * supported
166 * @param supported_styles bitflag with supported types
167 */
168 void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
169
170 /// Sets the connected status to true
171 void Connect();
172
173 /// Sets the connected status to false
174 void Disconnect();
175
176 /**
177 * Is the emulated connected
178 * @param get_temporary_value If true tmp_is_connected will be returned
179 * @return true if the controller has the connected status
180 */
181 bool IsConnected(bool get_temporary_value = false) const;
182
183 /// Returns true if vibration is enabled
184 bool IsVibrationEnabled() const;
185
186 /// Removes all callbacks created from input devices
187 void UnloadInput();
188
189 /**
190 * Sets the emulated controller into configuring mode
191 * This prevents the modification of the HID state of the emulated controller by input commands
192 */
193 void EnableConfiguration();
194
195 /// Returns the emulated controller into normal mode, allowing the modification of the HID state
196 void DisableConfiguration();
197
198 /// Returns true if the emulated controller is in configuring mode
199 bool IsConfiguring() const;
200
201 /// Reload all input devices
202 void ReloadInput();
203
204 /// Overrides current mapped devices with the stored configuration and reloads all input devices
205 void ReloadFromSettings();
206
207 /// Saves the current mapped configuration
208 void SaveCurrentConfig();
209
210 /// Reverts any mapped changes made that weren't saved
211 void RestoreConfig();
212
213 /// Returns a vector of mapped devices from the mapped button and stick parameters
214 std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const;
215
216 // Returns the current mapped button device
217 Common::ParamPackage GetButtonParam(std::size_t index) const;
218
219 // Returns the current mapped stick device
220 Common::ParamPackage GetStickParam(std::size_t index) const;
221
222 // Returns the current mapped motion device
223 Common::ParamPackage GetMotionParam(std::size_t index) const;
224
225 /**
226 * Updates the current mapped button device
227 * @param param ParamPackage with controller data to be mapped
228 */
229 void SetButtonParam(std::size_t index, Common::ParamPackage param);
230
231 /**
232 * Updates the current mapped stick device
233 * @param param ParamPackage with controller data to be mapped
234 */
235 void SetStickParam(std::size_t index, Common::ParamPackage param);
236
237 /**
238 * Updates the current mapped motion device
239 * @param param ParamPackage with controller data to be mapped
240 */
241 void SetMotionParam(std::size_t index, Common::ParamPackage param);
242
243 /// Returns the latest button status from the controller with parameters
244 ButtonValues GetButtonsValues() const;
245
246 /// Returns the latest analog stick status from the controller with parameters
247 SticksValues GetSticksValues() const;
248
249 /// Returns the latest trigger status from the controller with parameters
250 TriggerValues GetTriggersValues() const;
251
252 /// Returns the latest motion status from the controller with parameters
253 ControllerMotionValues GetMotionValues() const;
254
255 /// Returns the latest color status from the controller with parameters
256 ColorValues GetColorsValues() const;
257
258 /// Returns the latest battery status from the controller with parameters
259 BatteryValues GetBatteryValues() const;
260
261 /// Returns the latest status of button input for the npad service
262 NpadButtonState GetNpadButtons() const;
263
264 /// Returns the latest status of button input for the debug pad service
265 DebugPadButton GetDebugPadButtons() const;
266
267 /// Returns the latest status of stick input from the mouse
268 AnalogSticks GetSticks() const;
269
270 /// Returns the latest status of trigger input from the mouse
271 NpadGcTriggerState GetTriggers() const;
272
273 /// Returns the latest status of motion input from the mouse
274 MotionState GetMotions() const;
275
276 /// Returns the latest color value from the controller
277 ControllerColors GetColors() const;
278
279 /// Returns the latest battery status from the controller
280 BatteryLevelState GetBattery() const;
281
282 /**
283 * Sends a specific vibration to the output device
284 * @return returns true if vibration had no errors
285 */
286 bool SetVibration(std::size_t device_index, VibrationValue vibration);
287
288 /**
289 * Sends a small vibration to the output device
290 * @return returns true if SetVibration was successfull
291 */
292 bool TestVibration(std::size_t device_index);
293
294 /// Returns the led pattern corresponding to this emulated controller
295 LedPattern GetLedPattern() const;
296
297 /// Asks the output device to change the player led pattern
298 void SetLedPattern();
299
300 /**
301 * Adds a callback to the list of events
302 * @param update_callback A ConsoleUpdateCallback that will be triggered
303 * @return an unique key corresponding to the callback index in the list
304 */
305 int SetCallback(ControllerUpdateCallback update_callback);
306
307 /**
308 * Removes a callback from the list stopping any future events to this object
309 * @param key Key corresponding to the callback index in the list
310 */
311 void DeleteCallback(int key);
312
313private:
314 /// creates input devices from params
315 void LoadDevices();
316
317 /// Set the params for TAS devices
318 void LoadTASParams();
319
320 /**
321 * Checks the current controller type against the supported_style_tag
322 * @return true if the controller is supported
323 */
324 bool IsControllerSupported() const;
325
326 /**
327 * Updates the button status of the controller
328 * @param callback A CallbackStatus containing the button status
329 * @param index Button ID of the to be updated
330 */
331 void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
332 Common::UUID uuid);
333
334 /**
335 * Updates the analog stick status of the controller
336 * @param callback A CallbackStatus containing the analog stick status
337 * @param index stick ID of the to be updated
338 */
339 void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
340 Common::UUID uuid);
341
342 /**
343 * Updates the trigger status of the controller
344 * @param callback A CallbackStatus containing the trigger status
345 * @param index trigger ID of the to be updated
346 */
347 void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
348 Common::UUID uuid);
349
350 /**
351 * Updates the motion status of the controller
352 * @param callback A CallbackStatus containing gyro and accelerometer data
353 * @param index motion ID of the to be updated
354 */
355 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
356
357 /**
358 * Updates the battery status of the controller
359 * @param callback A CallbackStatus containing the battery status
360 * @param index Button ID of the to be updated
361 */
362 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
363
364 /**
365 * Triggers a callback that something has changed on the controller status
366 * @param type Input type of the event to trigger
367 * @param is_service_update indicates if this event should only be sent to HID services
368 */
369 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
370
371 NpadIdType npad_id_type;
372 NpadStyleIndex npad_type{NpadStyleIndex::None};
373 NpadStyleTag supported_style_tag{NpadStyleSet::All};
374 bool is_connected{false};
375 bool is_configuring{false};
376 f32 motion_sensitivity{0.01f};
377 bool force_update_motion{false};
378
379 // Temporary values to avoid doing changes while the controller is in configuring mode
380 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
381 bool tmp_is_connected{false};
382
383 ButtonParams button_params;
384 StickParams stick_params;
385 ControllerMotionParams motion_params;
386 TriggerParams trigger_params;
387 BatteryParams battery_params;
388 OutputParams output_params;
389
390 ButtonDevices button_devices;
391 StickDevices stick_devices;
392 ControllerMotionDevices motion_devices;
393 TriggerDevices trigger_devices;
394 BatteryDevices battery_devices;
395 OutputDevices output_devices;
396
397 // TAS related variables
398 ButtonParams tas_button_params;
399 StickParams tas_stick_params;
400 ButtonDevices tas_button_devices;
401 StickDevices tas_stick_devices;
402
403 mutable std::mutex mutex;
404 std::unordered_map<int, ControllerUpdateCallback> callback_list;
405 int last_callback_key = 0;
406
407 // Stores the current status of all controller input
408 ControllerStatus controller;
409};
410
411} // namespace Core::HID
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
new file mode 100644
index 000000000..708480f2d
--- /dev/null
+++ b/src/core/hid/emulated_devices.cpp
@@ -0,0 +1,459 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <algorithm>
6#include <fmt/format.h>
7
8#include "core/hid/emulated_devices.h"
9#include "core/hid/input_converter.h"
10
11namespace Core::HID {
12
13EmulatedDevices::EmulatedDevices() = default;
14
15EmulatedDevices::~EmulatedDevices() = default;
16
17void EmulatedDevices::ReloadFromSettings() {
18 ReloadInput();
19}
20
21void EmulatedDevices::ReloadInput() {
22 // If you load any device here add the equivalent to the UnloadInput() function
23 std::size_t key_index = 0;
24 for (auto& mouse_device : mouse_button_devices) {
25 Common::ParamPackage mouse_params;
26 mouse_params.Set("engine", "mouse");
27 mouse_params.Set("button", static_cast<int>(key_index));
28 mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
29 key_index++;
30 }
31
32 mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
33 "engine:mouse,axis_x:0,axis_y:1");
34
35 // First two axis are reserved for mouse position
36 key_index = 2;
37 for (auto& mouse_device : mouse_analog_devices) {
38 Common::ParamPackage mouse_params;
39 mouse_params.Set("engine", "mouse");
40 mouse_params.Set("axis", static_cast<int>(key_index));
41 mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
42 key_index++;
43 }
44
45 key_index = 0;
46 for (auto& keyboard_device : keyboard_devices) {
47 // Keyboard keys are only mapped on port 1, pad 0
48 Common::ParamPackage keyboard_params;
49 keyboard_params.Set("engine", "keyboard");
50 keyboard_params.Set("button", static_cast<int>(key_index));
51 keyboard_params.Set("port", 1);
52 keyboard_params.Set("pad", 0);
53 keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
54 key_index++;
55 }
56
57 key_index = 0;
58 for (auto& keyboard_device : keyboard_modifier_devices) {
59 // Keyboard moddifiers are only mapped on port 1, pad 1
60 Common::ParamPackage keyboard_params;
61 keyboard_params.Set("engine", "keyboard");
62 keyboard_params.Set("button", static_cast<int>(key_index));
63 keyboard_params.Set("port", 1);
64 keyboard_params.Set("pad", 1);
65 keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
66 key_index++;
67 }
68
69 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
70 if (!mouse_button_devices[index]) {
71 continue;
72 }
73 mouse_button_devices[index]->SetCallback({
74 .on_change =
75 [this, index](const Common::Input::CallbackStatus& callback) {
76 SetMouseButton(callback, index);
77 },
78 });
79 }
80
81 for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) {
82 if (!mouse_analog_devices[index]) {
83 continue;
84 }
85 mouse_analog_devices[index]->SetCallback({
86 .on_change =
87 [this, index](const Common::Input::CallbackStatus& callback) {
88 SetMouseAnalog(callback, index);
89 },
90 });
91 }
92
93 if (mouse_stick_device) {
94 mouse_stick_device->SetCallback({
95 .on_change =
96 [this](const Common::Input::CallbackStatus& callback) { SetMouseStick(callback); },
97 });
98 }
99
100 for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
101 if (!keyboard_devices[index]) {
102 continue;
103 }
104 keyboard_devices[index]->SetCallback({
105 .on_change =
106 [this, index](const Common::Input::CallbackStatus& callback) {
107 SetKeyboardButton(callback, index);
108 },
109 });
110 }
111
112 for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
113 if (!keyboard_modifier_devices[index]) {
114 continue;
115 }
116 keyboard_modifier_devices[index]->SetCallback({
117 .on_change =
118 [this, index](const Common::Input::CallbackStatus& callback) {
119 SetKeyboardModifier(callback, index);
120 },
121 });
122 }
123}
124
125void EmulatedDevices::UnloadInput() {
126 for (auto& button : mouse_button_devices) {
127 button.reset();
128 }
129 for (auto& analog : mouse_analog_devices) {
130 analog.reset();
131 }
132 mouse_stick_device.reset();
133 for (auto& button : keyboard_devices) {
134 button.reset();
135 }
136 for (auto& button : keyboard_modifier_devices) {
137 button.reset();
138 }
139}
140
141void EmulatedDevices::EnableConfiguration() {
142 is_configuring = true;
143 SaveCurrentConfig();
144}
145
146void EmulatedDevices::DisableConfiguration() {
147 is_configuring = false;
148}
149
150bool EmulatedDevices::IsConfiguring() const {
151 return is_configuring;
152}
153
154void EmulatedDevices::SaveCurrentConfig() {
155 if (!is_configuring) {
156 return;
157 }
158}
159
160void EmulatedDevices::RestoreConfig() {
161 if (!is_configuring) {
162 return;
163 }
164 ReloadFromSettings();
165}
166
167void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
168 std::size_t index) {
169 if (index >= device_status.keyboard_values.size()) {
170 return;
171 }
172 std::lock_guard lock{mutex};
173 bool value_changed = false;
174 const auto new_status = TransformToButton(callback);
175 auto& current_status = device_status.keyboard_values[index];
176 current_status.toggle = new_status.toggle;
177
178 // Update button status with current status
179 if (!current_status.toggle) {
180 current_status.locked = false;
181 if (current_status.value != new_status.value) {
182 current_status.value = new_status.value;
183 value_changed = true;
184 }
185 } else {
186 // Toggle button and lock status
187 if (new_status.value && !current_status.locked) {
188 current_status.locked = true;
189 current_status.value = !current_status.value;
190 value_changed = true;
191 }
192
193 // Unlock button, ready for next press
194 if (!new_status.value && current_status.locked) {
195 current_status.locked = false;
196 }
197 }
198
199 if (!value_changed) {
200 return;
201 }
202
203 if (is_configuring) {
204 TriggerOnChange(DeviceTriggerType::Keyboard);
205 return;
206 }
207
208 // Index should be converted from NativeKeyboard to KeyboardKeyIndex
209 UpdateKey(index, current_status.value);
210
211 TriggerOnChange(DeviceTriggerType::Keyboard);
212}
213
214void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
215 constexpr std::size_t KEYS_PER_BYTE = 8;
216 auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE];
217 const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE));
218 if (status) {
219 entry = entry | mask;
220 } else {
221 entry = static_cast<u8>(entry & ~mask);
222 }
223}
224
225void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
226 std::size_t index) {
227 if (index >= device_status.keyboard_moddifier_values.size()) {
228 return;
229 }
230 std::lock_guard lock{mutex};
231 bool value_changed = false;
232 const auto new_status = TransformToButton(callback);
233 auto& current_status = device_status.keyboard_moddifier_values[index];
234 current_status.toggle = new_status.toggle;
235
236 // Update button status with current
237 if (!current_status.toggle) {
238 current_status.locked = false;
239 if (current_status.value != new_status.value) {
240 current_status.value = new_status.value;
241 value_changed = true;
242 }
243 } else {
244 // Toggle button and lock status
245 if (new_status.value && !current_status.locked) {
246 current_status.locked = true;
247 current_status.value = !current_status.value;
248 value_changed = true;
249 }
250
251 // Unlock button ready for next press
252 if (!new_status.value && current_status.locked) {
253 current_status.locked = false;
254 }
255 }
256
257 if (!value_changed) {
258 return;
259 }
260
261 if (is_configuring) {
262 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
263 return;
264 }
265
266 switch (index) {
267 case Settings::NativeKeyboard::LeftControl:
268 case Settings::NativeKeyboard::RightControl:
269 device_status.keyboard_moddifier_state.control.Assign(current_status.value);
270 break;
271 case Settings::NativeKeyboard::LeftShift:
272 case Settings::NativeKeyboard::RightShift:
273 device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
274 break;
275 case Settings::NativeKeyboard::LeftAlt:
276 device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
277 break;
278 case Settings::NativeKeyboard::RightAlt:
279 device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
280 break;
281 case Settings::NativeKeyboard::CapsLock:
282 device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
283 break;
284 case Settings::NativeKeyboard::ScrollLock:
285 device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
286 break;
287 case Settings::NativeKeyboard::NumLock:
288 device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
289 break;
290 }
291
292 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
293}
294
295void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
296 std::size_t index) {
297 if (index >= device_status.mouse_button_values.size()) {
298 return;
299 }
300 std::lock_guard lock{mutex};
301 bool value_changed = false;
302 const auto new_status = TransformToButton(callback);
303 auto& current_status = device_status.mouse_button_values[index];
304 current_status.toggle = new_status.toggle;
305
306 // Update button status with current
307 if (!current_status.toggle) {
308 current_status.locked = false;
309 if (current_status.value != new_status.value) {
310 current_status.value = new_status.value;
311 value_changed = true;
312 }
313 } else {
314 // Toggle button and lock status
315 if (new_status.value && !current_status.locked) {
316 current_status.locked = true;
317 current_status.value = !current_status.value;
318 value_changed = true;
319 }
320
321 // Unlock button ready for next press
322 if (!new_status.value && current_status.locked) {
323 current_status.locked = false;
324 }
325 }
326
327 if (!value_changed) {
328 return;
329 }
330
331 if (is_configuring) {
332 TriggerOnChange(DeviceTriggerType::Mouse);
333 return;
334 }
335
336 switch (index) {
337 case Settings::NativeMouseButton::Left:
338 device_status.mouse_button_state.left.Assign(current_status.value);
339 break;
340 case Settings::NativeMouseButton::Right:
341 device_status.mouse_button_state.right.Assign(current_status.value);
342 break;
343 case Settings::NativeMouseButton::Middle:
344 device_status.mouse_button_state.middle.Assign(current_status.value);
345 break;
346 case Settings::NativeMouseButton::Forward:
347 device_status.mouse_button_state.forward.Assign(current_status.value);
348 break;
349 case Settings::NativeMouseButton::Back:
350 device_status.mouse_button_state.back.Assign(current_status.value);
351 break;
352 }
353
354 TriggerOnChange(DeviceTriggerType::Mouse);
355}
356
357void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callback,
358 std::size_t index) {
359 if (index >= device_status.mouse_analog_values.size()) {
360 return;
361 }
362 std::lock_guard lock{mutex};
363 const auto analog_value = TransformToAnalog(callback);
364
365 device_status.mouse_analog_values[index] = analog_value;
366
367 if (is_configuring) {
368 device_status.mouse_position_state = {};
369 TriggerOnChange(DeviceTriggerType::Mouse);
370 return;
371 }
372
373 switch (index) {
374 case Settings::NativeMouseWheel::X:
375 device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value);
376 break;
377 case Settings::NativeMouseWheel::Y:
378 device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value);
379 break;
380 }
381
382 TriggerOnChange(DeviceTriggerType::Mouse);
383}
384
385void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
386 std::lock_guard lock{mutex};
387 const auto touch_value = TransformToTouch(callback);
388
389 device_status.mouse_stick_value = touch_value;
390
391 if (is_configuring) {
392 device_status.mouse_position_state = {};
393 TriggerOnChange(DeviceTriggerType::Mouse);
394 return;
395 }
396
397 device_status.mouse_position_state.x = touch_value.x.value;
398 device_status.mouse_position_state.y = touch_value.y.value;
399
400 TriggerOnChange(DeviceTriggerType::Mouse);
401}
402
403KeyboardValues EmulatedDevices::GetKeyboardValues() const {
404 return device_status.keyboard_values;
405}
406
407KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
408 return device_status.keyboard_moddifier_values;
409}
410
411MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
412 return device_status.mouse_button_values;
413}
414
415KeyboardKey EmulatedDevices::GetKeyboard() const {
416 return device_status.keyboard_state;
417}
418
419KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
420 return device_status.keyboard_moddifier_state;
421}
422
423MouseButton EmulatedDevices::GetMouseButtons() const {
424 return device_status.mouse_button_state;
425}
426
427MousePosition EmulatedDevices::GetMousePosition() const {
428 return device_status.mouse_position_state;
429}
430
431AnalogStickState EmulatedDevices::GetMouseWheel() const {
432 return device_status.mouse_wheel_state;
433}
434
435void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
436 for (const auto& poller_pair : callback_list) {
437 const InterfaceUpdateCallback& poller = poller_pair.second;
438 if (poller.on_change) {
439 poller.on_change(type);
440 }
441 }
442}
443
444int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
445 std::lock_guard lock{mutex};
446 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
447 return last_callback_key++;
448}
449
450void EmulatedDevices::DeleteCallback(int key) {
451 std::lock_guard lock{mutex};
452 const auto& iterator = callback_list.find(key);
453 if (iterator == callback_list.end()) {
454 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
455 return;
456 }
457 callback_list.erase(iterator);
458}
459} // namespace Core::HID
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
new file mode 100644
index 000000000..790d3b411
--- /dev/null
+++ b/src/core/hid/emulated_devices.h
@@ -0,0 +1,210 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/settings.h"
17#include "core/hid/hid_types.h"
18
19namespace Core::HID {
20using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
21 Settings::NativeKeyboard::NumKeyboardKeys>;
22using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
23 Settings::NativeKeyboard::NumKeyboardMods>;
24using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
25 Settings::NativeMouseButton::NumMouseButtons>;
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29
30using MouseButtonParams =
31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
32
33using KeyboardValues =
34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
35using KeyboardModifierValues =
36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
37using MouseButtonValues =
38 std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
39using MouseAnalogValues =
40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
41using MouseStickValue = Common::Input::TouchStatus;
42
43struct MousePosition {
44 f32 x;
45 f32 y;
46};
47
48struct DeviceStatus {
49 // Data from input_common
50 KeyboardValues keyboard_values{};
51 KeyboardModifierValues keyboard_moddifier_values{};
52 MouseButtonValues mouse_button_values{};
53 MouseAnalogValues mouse_analog_values{};
54 MouseStickValue mouse_stick_value{};
55
56 // Data for HID serices
57 KeyboardKey keyboard_state{};
58 KeyboardModifier keyboard_moddifier_state{};
59 MouseButton mouse_button_state{};
60 MousePosition mouse_position_state{};
61 AnalogStickState mouse_wheel_state{};
62};
63
64enum class DeviceTriggerType {
65 Keyboard,
66 KeyboardModdifier,
67 Mouse,
68};
69
70struct InterfaceUpdateCallback {
71 std::function<void(DeviceTriggerType)> on_change;
72};
73
74class EmulatedDevices {
75public:
76 /**
77 * Contains all input data related to external devices that aren't necesarily a controller
78 * This includes devices such as the keyboard or mouse
79 */
80 explicit EmulatedDevices();
81 ~EmulatedDevices();
82
83 YUZU_NON_COPYABLE(EmulatedDevices);
84 YUZU_NON_MOVEABLE(EmulatedDevices);
85
86 /// Removes all callbacks created from input devices
87 void UnloadInput();
88
89 /**
90 * Sets the emulated devices into configuring mode
91 * This prevents the modification of the HID state of the emulated devices by input commands
92 */
93 void EnableConfiguration();
94
95 /// Returns the emulated devices into normal mode, allowing the modification of the HID state
96 void DisableConfiguration();
97
98 /// Returns true if the emulated device is in configuring mode
99 bool IsConfiguring() const;
100
101 /// Reload all input devices
102 void ReloadInput();
103
104 /// Overrides current mapped devices with the stored configuration and reloads all input devices
105 void ReloadFromSettings();
106
107 /// Saves the current mapped configuration
108 void SaveCurrentConfig();
109
110 /// Reverts any mapped changes made that weren't saved
111 void RestoreConfig();
112
113 /// Returns the latest status of button input from the keyboard with parameters
114 KeyboardValues GetKeyboardValues() const;
115
116 /// Returns the latest status of button input from the keyboard modifiers with parameters
117 KeyboardModifierValues GetKeyboardModdifierValues() const;
118
119 /// Returns the latest status of button input from the mouse with parameters
120 MouseButtonValues GetMouseButtonsValues() const;
121
122 /// Returns the latest status of button input from the keyboard
123 KeyboardKey GetKeyboard() const;
124
125 /// Returns the latest status of button input from the keyboard modifiers
126 KeyboardModifier GetKeyboardModifier() const;
127
128 /// Returns the latest status of button input from the mouse
129 MouseButton GetMouseButtons() const;
130
131 /// Returns the latest mouse coordinates
132 MousePosition GetMousePosition() const;
133
134 /// Returns the latest mouse wheel change
135 AnalogStickState GetMouseWheel() const;
136
137 /**
138 * Adds a callback to the list of events
139 * @param update_callback InterfaceUpdateCallback that will be triggered
140 * @return an unique key corresponding to the callback index in the list
141 */
142 int SetCallback(InterfaceUpdateCallback update_callback);
143
144 /**
145 * Removes a callback from the list stopping any future events to this object
146 * @param key Key corresponding to the callback index in the list
147 */
148 void DeleteCallback(int key);
149
150private:
151 /// Helps assigning a value to keyboard_state
152 void UpdateKey(std::size_t key_index, bool status);
153
154 /**
155 * Updates the touch status of the keyboard device
156 * @param callback A CallbackStatus containing the key status
157 * @param index key ID to be updated
158 */
159 void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);
160
161 /**
162 * Updates the keyboard status of the keyboard device
163 * @param callback A CallbackStatus containing the modifier key status
164 * @param index modifier key ID to be updated
165 */
166 void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);
167
168 /**
169 * Updates the mouse button status of the mouse device
170 * @param callback A CallbackStatus containing the button status
171 * @param index Button ID to be updated
172 */
173 void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);
174
175 /**
176 * Updates the mouse wheel status of the mouse device
177 * @param callback A CallbackStatus containing the wheel status
178 * @param index wheel ID to be updated
179 */
180 void SetMouseAnalog(const Common::Input::CallbackStatus& callback, std::size_t index);
181
182 /**
183 * Updates the mouse position status of the mouse device
184 * @param callback A CallbackStatus containing the position status
185 */
186 void SetMouseStick(const Common::Input::CallbackStatus& callback);
187
188 /**
189 * Triggers a callback that something has changed on the device status
190 * @param type Input type of the event to trigger
191 */
192 void TriggerOnChange(DeviceTriggerType type);
193
194 bool is_configuring{false};
195
196 KeyboardDevices keyboard_devices;
197 KeyboardModifierDevices keyboard_modifier_devices;
198 MouseButtonDevices mouse_button_devices;
199 MouseAnalogDevices mouse_analog_devices;
200 MouseStickDevice mouse_stick_device;
201
202 mutable std::mutex mutex;
203 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
204 int last_callback_key = 0;
205
206 // Stores the current status of all external device input
207 DeviceStatus device_status;
208};
209
210} // namespace Core::HID
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
new file mode 100644
index 000000000..a1c3bbb57
--- /dev/null
+++ b/src/core/hid/hid_core.cpp
@@ -0,0 +1,214 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/hid/emulated_console.h"
7#include "core/hid/emulated_controller.h"
8#include "core/hid/emulated_devices.h"
9#include "core/hid/hid_core.h"
10
11namespace Core::HID {
12
13HIDCore::HIDCore()
14 : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
15 player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
16 player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
17 player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
18 player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
19 player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
20 player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
21 player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
22 other{std::make_unique<EmulatedController>(NpadIdType::Other)},
23 handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
24 console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
25
26HIDCore::~HIDCore() = default;
27
28EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
29 switch (npad_id_type) {
30 case NpadIdType::Player1:
31 return player_1.get();
32 case NpadIdType::Player2:
33 return player_2.get();
34 case NpadIdType::Player3:
35 return player_3.get();
36 case NpadIdType::Player4:
37 return player_4.get();
38 case NpadIdType::Player5:
39 return player_5.get();
40 case NpadIdType::Player6:
41 return player_6.get();
42 case NpadIdType::Player7:
43 return player_7.get();
44 case NpadIdType::Player8:
45 return player_8.get();
46 case NpadIdType::Other:
47 return other.get();
48 case NpadIdType::Handheld:
49 return handheld.get();
50 case NpadIdType::Invalid:
51 default:
52 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
53 return nullptr;
54 }
55}
56
57const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
58 switch (npad_id_type) {
59 case NpadIdType::Player1:
60 return player_1.get();
61 case NpadIdType::Player2:
62 return player_2.get();
63 case NpadIdType::Player3:
64 return player_3.get();
65 case NpadIdType::Player4:
66 return player_4.get();
67 case NpadIdType::Player5:
68 return player_5.get();
69 case NpadIdType::Player6:
70 return player_6.get();
71 case NpadIdType::Player7:
72 return player_7.get();
73 case NpadIdType::Player8:
74 return player_8.get();
75 case NpadIdType::Other:
76 return other.get();
77 case NpadIdType::Handheld:
78 return handheld.get();
79 case NpadIdType::Invalid:
80 default:
81 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
82 return nullptr;
83 }
84}
85EmulatedConsole* HIDCore::GetEmulatedConsole() {
86 return console.get();
87}
88
89const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
90 return console.get();
91}
92
93EmulatedDevices* HIDCore::GetEmulatedDevices() {
94 return devices.get();
95}
96
97const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
98 return devices.get();
99}
100
101EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
102 return GetEmulatedController(IndexToNpadIdType(index));
103}
104
105const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
106 return GetEmulatedController(IndexToNpadIdType(index));
107}
108
109void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
110 supported_style_tag.raw = style_tag.raw;
111 player_1->SetSupportedNpadStyleTag(supported_style_tag);
112 player_2->SetSupportedNpadStyleTag(supported_style_tag);
113 player_3->SetSupportedNpadStyleTag(supported_style_tag);
114 player_4->SetSupportedNpadStyleTag(supported_style_tag);
115 player_5->SetSupportedNpadStyleTag(supported_style_tag);
116 player_6->SetSupportedNpadStyleTag(supported_style_tag);
117 player_7->SetSupportedNpadStyleTag(supported_style_tag);
118 player_8->SetSupportedNpadStyleTag(supported_style_tag);
119 other->SetSupportedNpadStyleTag(supported_style_tag);
120 handheld->SetSupportedNpadStyleTag(supported_style_tag);
121}
122
123NpadStyleTag HIDCore::GetSupportedStyleTag() const {
124 return supported_style_tag;
125}
126
127s8 HIDCore::GetPlayerCount() const {
128 s8 active_players = 0;
129 for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) {
130 const auto* const controller = GetEmulatedControllerByIndex(player_index);
131 if (controller->IsConnected()) {
132 active_players++;
133 }
134 }
135 return active_players;
136}
137
138NpadIdType HIDCore::GetFirstNpadId() const {
139 for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
140 const auto* const controller = GetEmulatedControllerByIndex(player_index);
141 if (controller->IsConnected()) {
142 return controller->GetNpadIdType();
143 }
144 }
145 return NpadIdType::Player1;
146}
147
148NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
149 for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
150 const auto* const controller = GetEmulatedControllerByIndex(player_index);
151 if (!controller->IsConnected()) {
152 return controller->GetNpadIdType();
153 }
154 }
155 return NpadIdType::Player1;
156}
157
158void HIDCore::EnableAllControllerConfiguration() {
159 player_1->EnableConfiguration();
160 player_2->EnableConfiguration();
161 player_3->EnableConfiguration();
162 player_4->EnableConfiguration();
163 player_5->EnableConfiguration();
164 player_6->EnableConfiguration();
165 player_7->EnableConfiguration();
166 player_8->EnableConfiguration();
167 other->EnableConfiguration();
168 handheld->EnableConfiguration();
169}
170
171void HIDCore::DisableAllControllerConfiguration() {
172 player_1->DisableConfiguration();
173 player_2->DisableConfiguration();
174 player_3->DisableConfiguration();
175 player_4->DisableConfiguration();
176 player_5->DisableConfiguration();
177 player_6->DisableConfiguration();
178 player_7->DisableConfiguration();
179 player_8->DisableConfiguration();
180 other->DisableConfiguration();
181 handheld->DisableConfiguration();
182}
183
184void HIDCore::ReloadInputDevices() {
185 player_1->ReloadFromSettings();
186 player_2->ReloadFromSettings();
187 player_3->ReloadFromSettings();
188 player_4->ReloadFromSettings();
189 player_5->ReloadFromSettings();
190 player_6->ReloadFromSettings();
191 player_7->ReloadFromSettings();
192 player_8->ReloadFromSettings();
193 other->ReloadFromSettings();
194 handheld->ReloadFromSettings();
195 console->ReloadFromSettings();
196 devices->ReloadFromSettings();
197}
198
199void HIDCore::UnloadInputDevices() {
200 player_1->UnloadInput();
201 player_2->UnloadInput();
202 player_3->UnloadInput();
203 player_4->UnloadInput();
204 player_5->UnloadInput();
205 player_6->UnloadInput();
206 player_7->UnloadInput();
207 player_8->UnloadInput();
208 other->UnloadInput();
209 handheld->UnloadInput();
210 console->UnloadInput();
211 devices->UnloadInput();
212}
213
214} // namespace Core::HID
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
new file mode 100644
index 000000000..837f7de49
--- /dev/null
+++ b/src/core/hid/hid_core.h
@@ -0,0 +1,82 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include "core/hid/hid_types.h"
10
11namespace Core::HID {
12class EmulatedConsole;
13class EmulatedController;
14class EmulatedDevices;
15} // namespace Core::HID
16
17namespace Core::HID {
18
19class HIDCore {
20public:
21 explicit HIDCore();
22 ~HIDCore();
23
24 YUZU_NON_COPYABLE(HIDCore);
25 YUZU_NON_MOVEABLE(HIDCore);
26
27 EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
28 const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;
29
30 EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
31 const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
32
33 EmulatedConsole* GetEmulatedConsole();
34 const EmulatedConsole* GetEmulatedConsole() const;
35
36 EmulatedDevices* GetEmulatedDevices();
37 const EmulatedDevices* GetEmulatedDevices() const;
38
39 void SetSupportedStyleTag(NpadStyleTag style_tag);
40 NpadStyleTag GetSupportedStyleTag() const;
41
42 /// Counts the connected players from P1-P8
43 s8 GetPlayerCount() const;
44
45 /// Returns the first connected npad id
46 NpadIdType GetFirstNpadId() const;
47
48 /// Returns the first disconnected npad id
49 NpadIdType GetFirstDisconnectedNpadId() const;
50
51 /// Sets all emulated controllers into configuring mode.
52 void EnableAllControllerConfiguration();
53
54 /// Sets all emulated controllers into normal mode.
55 void DisableAllControllerConfiguration();
56
57 /// Reloads all input devices from settings
58 void ReloadInputDevices();
59
60 /// Removes all callbacks from input common
61 void UnloadInputDevices();
62
63 /// Number of emulated controllers
64 static constexpr std::size_t available_controllers{10};
65
66private:
67 std::unique_ptr<EmulatedController> player_1;
68 std::unique_ptr<EmulatedController> player_2;
69 std::unique_ptr<EmulatedController> player_3;
70 std::unique_ptr<EmulatedController> player_4;
71 std::unique_ptr<EmulatedController> player_5;
72 std::unique_ptr<EmulatedController> player_6;
73 std::unique_ptr<EmulatedController> player_7;
74 std::unique_ptr<EmulatedController> player_8;
75 std::unique_ptr<EmulatedController> other;
76 std::unique_ptr<EmulatedController> handheld;
77 std::unique_ptr<EmulatedConsole> console;
78 std::unique_ptr<EmulatedDevices> devices;
79 NpadStyleTag supported_style_tag{NpadStyleSet::All};
80};
81
82} // namespace Core::HID
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
new file mode 100644
index 000000000..7c12f01fc
--- /dev/null
+++ b/src/core/hid/hid_types.h
@@ -0,0 +1,635 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/bit_field.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/point.h"
11#include "common/uuid.h"
12
13namespace Core::HID {
14
15enum class DeviceIndex : u8 {
16 Left = 0,
17 Right = 1,
18 None = 2,
19 MaxDeviceIndex = 3,
20};
21
22// This is nn::hid::NpadButton
23enum class NpadButton : u64 {
24 None = 0,
25 A = 1U << 0,
26 B = 1U << 1,
27 X = 1U << 2,
28 Y = 1U << 3,
29 StickL = 1U << 4,
30 StickR = 1U << 5,
31 L = 1U << 6,
32 R = 1U << 7,
33 ZL = 1U << 8,
34 ZR = 1U << 9,
35 Plus = 1U << 10,
36 Minus = 1U << 11,
37
38 Left = 1U << 12,
39 Up = 1U << 13,
40 Right = 1U << 14,
41 Down = 1U << 15,
42
43 StickLLeft = 1U << 16,
44 StickLUp = 1U << 17,
45 StickLRight = 1U << 18,
46 StickLDown = 1U << 19,
47
48 StickRLeft = 1U << 20,
49 StickRUp = 1U << 21,
50 StickRRight = 1U << 22,
51 StickRDown = 1U << 23,
52
53 LeftSL = 1U << 24,
54 LeftSR = 1U << 25,
55
56 RightSL = 1U << 26,
57 RightSR = 1U << 27,
58
59 Palma = 1U << 28,
60 Verification = 1U << 29,
61 HandheldLeftB = 1U << 30,
62 LagonCLeft = 1U << 31,
63 LagonCUp = 1ULL << 32,
64 LagonCRight = 1ULL << 33,
65 LagonCDown = 1ULL << 34,
66
67 All = 0xFFFFFFFFFFFFFFFFULL,
68};
69DECLARE_ENUM_FLAG_OPERATORS(NpadButton);
70
71enum class KeyboardKeyIndex : u32 {
72 A = 4,
73 B = 5,
74 C = 6,
75 D = 7,
76 E = 8,
77 F = 9,
78 G = 10,
79 H = 11,
80 I = 12,
81 J = 13,
82 K = 14,
83 L = 15,
84 M = 16,
85 N = 17,
86 O = 18,
87 P = 19,
88 Q = 20,
89 R = 21,
90 S = 22,
91 T = 23,
92 U = 24,
93 V = 25,
94 W = 26,
95 X = 27,
96 Y = 28,
97 Z = 29,
98 D1 = 30,
99 D2 = 31,
100 D3 = 32,
101 D4 = 33,
102 D5 = 34,
103 D6 = 35,
104 D7 = 36,
105 D8 = 37,
106 D9 = 38,
107 D0 = 39,
108 Return = 40,
109 Escape = 41,
110 Backspace = 42,
111 Tab = 43,
112 Space = 44,
113 Minus = 45,
114 Plus = 46,
115 OpenBracket = 47,
116 CloseBracket = 48,
117 Pipe = 49,
118 Tilde = 50,
119 Semicolon = 51,
120 Quote = 52,
121 Backquote = 53,
122 Comma = 54,
123 Period = 55,
124 Slash = 56,
125 CapsLock = 57,
126 F1 = 58,
127 F2 = 59,
128 F3 = 60,
129 F4 = 61,
130 F5 = 62,
131 F6 = 63,
132 F7 = 64,
133 F8 = 65,
134 F9 = 66,
135 F10 = 67,
136 F11 = 68,
137 F12 = 69,
138 PrintScreen = 70,
139 ScrollLock = 71,
140 Pause = 72,
141 Insert = 73,
142 Home = 74,
143 PageUp = 75,
144 Delete = 76,
145 End = 77,
146 PageDown = 78,
147 RightArrow = 79,
148 LeftArrow = 80,
149 DownArrow = 81,
150 UpArrow = 82,
151 NumLock = 83,
152 NumPadDivide = 84,
153 NumPadMultiply = 85,
154 NumPadSubtract = 86,
155 NumPadAdd = 87,
156 NumPadEnter = 88,
157 NumPad1 = 89,
158 NumPad2 = 90,
159 NumPad3 = 91,
160 NumPad4 = 92,
161 NumPad5 = 93,
162 NumPad6 = 94,
163 NumPad7 = 95,
164 NumPad8 = 96,
165 NumPad9 = 97,
166 NumPad0 = 98,
167 NumPadDot = 99,
168 Backslash = 100,
169 Application = 101,
170 Power = 102,
171 NumPadEquals = 103,
172 F13 = 104,
173 F14 = 105,
174 F15 = 106,
175 F16 = 107,
176 F17 = 108,
177 F18 = 109,
178 F19 = 110,
179 F20 = 111,
180 F21 = 112,
181 F22 = 113,
182 F23 = 114,
183 F24 = 115,
184 NumPadComma = 133,
185 Ro = 135,
186 KatakanaHiragana = 136,
187 Yen = 137,
188 Henkan = 138,
189 Muhenkan = 139,
190 NumPadCommaPc98 = 140,
191 HangulEnglish = 144,
192 Hanja = 145,
193 Katakana = 146,
194 Hiragana = 147,
195 ZenkakuHankaku = 148,
196 LeftControl = 224,
197 LeftShift = 225,
198 LeftAlt = 226,
199 LeftGui = 227,
200 RightControl = 228,
201 RightShift = 229,
202 RightAlt = 230,
203 RightGui = 231,
204};
205
206// This is nn::hid::NpadIdType
207enum class NpadIdType : u32 {
208 Player1 = 0x0,
209 Player2 = 0x1,
210 Player3 = 0x2,
211 Player4 = 0x3,
212 Player5 = 0x4,
213 Player6 = 0x5,
214 Player7 = 0x6,
215 Player8 = 0x7,
216 Other = 0x10,
217 Handheld = 0x20,
218
219 Invalid = 0xFFFFFFFF,
220};
221
222// This is nn::hid::NpadStyleIndex
223enum class NpadStyleIndex : u8 {
224 None = 0,
225 ProController = 3,
226 Handheld = 4,
227 HandheldNES = 4,
228 JoyconDual = 5,
229 JoyconLeft = 6,
230 JoyconRight = 7,
231 GameCube = 8,
232 Pokeball = 9,
233 NES = 10,
234 SNES = 12,
235 N64 = 13,
236 SegaGenesis = 14,
237 SystemExt = 32,
238 System = 33,
239 MaxNpadType = 34,
240};
241
242// This is nn::hid::NpadStyleSet
243enum class NpadStyleSet : u32 {
244 None = 0,
245 Fullkey = 1U << 0,
246 Handheld = 1U << 1,
247 JoyDual = 1U << 2,
248 JoyLeft = 1U << 3,
249 JoyRight = 1U << 4,
250 Gc = 1U << 5,
251 Palma = 1U << 6,
252 Lark = 1U << 7,
253 HandheldLark = 1U << 8,
254 Lucia = 1U << 9,
255 Lagoon = 1U << 10,
256 Lager = 1U << 11,
257 SystemExt = 1U << 29,
258 System = 1U << 30,
259
260 All = 0xFFFFFFFFU,
261};
262static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
263
264// This is nn::hid::VibrationDevicePosition
265enum class VibrationDevicePosition : u32 {
266 None = 0,
267 Left = 1,
268 Right = 2,
269};
270
271// This is nn::hid::VibrationDeviceType
272enum class VibrationDeviceType : u32 {
273 Unknown = 0,
274 LinearResonantActuator = 1,
275 GcErm = 2,
276};
277
278// This is nn::hid::VibrationGcErmCommand
279enum class VibrationGcErmCommand : u64 {
280 Stop = 0,
281 Start = 1,
282 StopHard = 2,
283};
284
285// This is nn::hid::NpadStyleTag
286struct NpadStyleTag {
287 union {
288 NpadStyleSet raw{};
289
290 BitField<0, 1, u32> fullkey;
291 BitField<1, 1, u32> handheld;
292 BitField<2, 1, u32> joycon_dual;
293 BitField<3, 1, u32> joycon_left;
294 BitField<4, 1, u32> joycon_right;
295 BitField<5, 1, u32> gamecube;
296 BitField<6, 1, u32> palma;
297 BitField<7, 1, u32> lark;
298 BitField<8, 1, u32> handheld_lark;
299 BitField<9, 1, u32> lucia;
300 BitField<10, 1, u32> lagoon;
301 BitField<11, 1, u32> lager;
302 BitField<29, 1, u32> system_ext;
303 BitField<30, 1, u32> system;
304 };
305};
306static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size");
307
308// This is nn::hid::TouchAttribute
309struct TouchAttribute {
310 union {
311 u32 raw{};
312 BitField<0, 1, u32> start_touch;
313 BitField<1, 1, u32> end_touch;
314 };
315};
316static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
317
318// This is nn::hid::TouchState
319struct TouchState {
320 u64 delta_time;
321 TouchAttribute attribute;
322 u32 finger;
323 Common::Point<u32> position;
324 u32 diameter_x;
325 u32 diameter_y;
326 u32 rotation_angle;
327};
328static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
329
330// This is nn::hid::NpadControllerColor
331struct NpadControllerColor {
332 u32 body;
333 u32 button;
334};
335static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
336
337// This is nn::hid::AnalogStickState
338struct AnalogStickState {
339 s32 x;
340 s32 y;
341};
342static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");
343
344// This is nn::hid::server::NpadGcTriggerState
345struct NpadGcTriggerState {
346 s64 sampling_number{};
347 s32 left{};
348 s32 right{};
349};
350static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
351
352// This is nn::hid::system::NpadBatteryLevel
353using NpadBatteryLevel = u32;
354static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
355
356// This is nn::hid::system::NpadPowerInfo
357struct NpadPowerInfo {
358 bool is_powered;
359 bool is_charging;
360 INSERT_PADDING_BYTES(0x6);
361 NpadBatteryLevel battery_level;
362};
363static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
364
365struct LedPattern {
366 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
367 position1.Assign(light1);
368 position2.Assign(light2);
369 position3.Assign(light3);
370 position4.Assign(light4);
371 }
372 union {
373 u64 raw{};
374 BitField<0, 1, u64> position1;
375 BitField<1, 1, u64> position2;
376 BitField<2, 1, u64> position3;
377 BitField<3, 1, u64> position4;
378 };
379};
380
381struct NpadButtonState {
382 union {
383 NpadButton raw{};
384
385 // Buttons
386 BitField<0, 1, u64> a;
387 BitField<1, 1, u64> b;
388 BitField<2, 1, u64> x;
389 BitField<3, 1, u64> y;
390 BitField<4, 1, u64> stick_l;
391 BitField<5, 1, u64> stick_r;
392 BitField<6, 1, u64> l;
393 BitField<7, 1, u64> r;
394 BitField<8, 1, u64> zl;
395 BitField<9, 1, u64> zr;
396 BitField<10, 1, u64> plus;
397 BitField<11, 1, u64> minus;
398
399 // D-Pad
400 BitField<12, 1, u64> left;
401 BitField<13, 1, u64> up;
402 BitField<14, 1, u64> right;
403 BitField<15, 1, u64> down;
404
405 // Left JoyStick
406 BitField<16, 1, u64> stick_l_left;
407 BitField<17, 1, u64> stick_l_up;
408 BitField<18, 1, u64> stick_l_right;
409 BitField<19, 1, u64> stick_l_down;
410
411 // Right JoyStick
412 BitField<20, 1, u64> stick_r_left;
413 BitField<21, 1, u64> stick_r_up;
414 BitField<22, 1, u64> stick_r_right;
415 BitField<23, 1, u64> stick_r_down;
416
417 BitField<24, 1, u64> left_sl;
418 BitField<25, 1, u64> left_sr;
419
420 BitField<26, 1, u64> right_sl;
421 BitField<27, 1, u64> right_sr;
422
423 BitField<28, 1, u64> palma;
424 BitField<29, 1, u64> verification;
425 BitField<30, 1, u64> handheld_left_b;
426 BitField<31, 1, u64> lagon_c_left;
427 BitField<32, 1, u64> lagon_c_up;
428 BitField<33, 1, u64> lagon_c_right;
429 BitField<34, 1, u64> lagon_c_down;
430 };
431};
432static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size.");
433
434// This is nn::hid::DebugPadButton
435struct DebugPadButton {
436 union {
437 u32 raw{};
438 BitField<0, 1, u32> a;
439 BitField<1, 1, u32> b;
440 BitField<2, 1, u32> x;
441 BitField<3, 1, u32> y;
442 BitField<4, 1, u32> l;
443 BitField<5, 1, u32> r;
444 BitField<6, 1, u32> zl;
445 BitField<7, 1, u32> zr;
446 BitField<8, 1, u32> plus;
447 BitField<9, 1, u32> minus;
448 BitField<10, 1, u32> d_left;
449 BitField<11, 1, u32> d_up;
450 BitField<12, 1, u32> d_right;
451 BitField<13, 1, u32> d_down;
452 };
453};
454static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size");
455
456// This is nn::hid::ConsoleSixAxisSensorHandle
457struct ConsoleSixAxisSensorHandle {
458 u8 unknown_1;
459 u8 unknown_2;
460 INSERT_PADDING_BYTES_NOINIT(2);
461};
462static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
463 "ConsoleSixAxisSensorHandle is an invalid size");
464
465// This is nn::hid::SixAxisSensorHandle
466struct SixAxisSensorHandle {
467 NpadStyleIndex npad_type;
468 u8 npad_id;
469 DeviceIndex device_index;
470 INSERT_PADDING_BYTES_NOINIT(1);
471};
472static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
473
474struct SixAxisSensorFusionParameters {
475 f32 parameter1;
476 f32 parameter2;
477};
478static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
479 "SixAxisSensorFusionParameters is an invalid size");
480
481// This is nn::hid::VibrationDeviceHandle
482struct VibrationDeviceHandle {
483 NpadStyleIndex npad_type;
484 u8 npad_id;
485 DeviceIndex device_index;
486 INSERT_PADDING_BYTES_NOINIT(1);
487};
488static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");
489
490// This is nn::hid::VibrationValue
491struct VibrationValue {
492 f32 low_amplitude;
493 f32 low_frequency;
494 f32 high_amplitude;
495 f32 high_frequency;
496};
497static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
498
499// This is nn::hid::VibrationDeviceInfo
500struct VibrationDeviceInfo {
501 VibrationDeviceType type{};
502 VibrationDevicePosition position{};
503};
504static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
505
506// This is nn::hid::KeyboardModifier
507struct KeyboardModifier {
508 union {
509 u32 raw{};
510 BitField<0, 1, u32> control;
511 BitField<1, 1, u32> shift;
512 BitField<2, 1, u32> left_alt;
513 BitField<3, 1, u32> right_alt;
514 BitField<4, 1, u32> gui;
515 BitField<8, 1, u32> caps_lock;
516 BitField<9, 1, u32> scroll_lock;
517 BitField<10, 1, u32> num_lock;
518 BitField<11, 1, u32> katakana;
519 BitField<12, 1, u32> hiragana;
520 };
521};
522
523static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size");
524
525// This is nn::hid::KeyboardAttribute
526struct KeyboardAttribute {
527 union {
528 u32 raw{};
529 BitField<0, 1, u32> is_connected;
530 };
531};
532static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size");
533
534// This is nn::hid::KeyboardKey
535struct KeyboardKey {
536 // This should be a 256 bit flag
537 std::array<u8, 32> key;
538};
539static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
540
541// This is nn::hid::MouseButton
542struct MouseButton {
543 union {
544 u32_le raw{};
545 BitField<0, 1, u32> left;
546 BitField<1, 1, u32> right;
547 BitField<2, 1, u32> middle;
548 BitField<3, 1, u32> forward;
549 BitField<4, 1, u32> back;
550 };
551};
552static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size");
553
554// This is nn::hid::MouseAttribute
555struct MouseAttribute {
556 union {
557 u32 raw{};
558 BitField<0, 1, u32> transferable;
559 BitField<1, 1, u32> is_connected;
560 };
561};
562static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size");
563
564// This is nn::hid::detail::MouseState
565struct MouseState {
566 s64 sampling_number;
567 s32 x;
568 s32 y;
569 s32 delta_x;
570 s32 delta_y;
571 // Axis Order in HW is switched for the wheel
572 s32 delta_wheel_y;
573 s32 delta_wheel_x;
574 MouseButton button;
575 MouseAttribute attribute;
576};
577static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
578
579/// Converts a NpadIdType to an array index.
580constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
581 switch (npad_id_type) {
582 case NpadIdType::Player1:
583 return 0;
584 case NpadIdType::Player2:
585 return 1;
586 case NpadIdType::Player3:
587 return 2;
588 case NpadIdType::Player4:
589 return 3;
590 case NpadIdType::Player5:
591 return 4;
592 case NpadIdType::Player6:
593 return 5;
594 case NpadIdType::Player7:
595 return 6;
596 case NpadIdType::Player8:
597 return 7;
598 case NpadIdType::Handheld:
599 return 8;
600 case NpadIdType::Other:
601 return 9;
602 default:
603 return 0;
604 }
605}
606
607/// Converts an array index to a NpadIdType
608constexpr NpadIdType IndexToNpadIdType(size_t index) {
609 switch (index) {
610 case 0:
611 return NpadIdType::Player1;
612 case 1:
613 return NpadIdType::Player2;
614 case 2:
615 return NpadIdType::Player3;
616 case 3:
617 return NpadIdType::Player4;
618 case 4:
619 return NpadIdType::Player5;
620 case 5:
621 return NpadIdType::Player6;
622 case 6:
623 return NpadIdType::Player7;
624 case 7:
625 return NpadIdType::Player8;
626 case 8:
627 return NpadIdType::Handheld;
628 case 9:
629 return NpadIdType::Other;
630 default:
631 return NpadIdType::Invalid;
632 }
633}
634
635} // namespace Core::HID
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
new file mode 100644
index 000000000..f5acff6e0
--- /dev/null
+++ b/src/core/hid/input_converter.cpp
@@ -0,0 +1,383 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <random>
6
7#include "common/input.h"
8#include "core/hid/input_converter.h"
9
10namespace Core::HID {
11
12Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) {
13 Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None};
14 switch (callback.type) {
15 case Common::Input::InputType::Analog:
16 case Common::Input::InputType::Trigger: {
17 const auto value = TransformToTrigger(callback).analog.value;
18 battery = Common::Input::BatteryLevel::Empty;
19 if (value > 0.2f) {
20 battery = Common::Input::BatteryLevel::Critical;
21 }
22 if (value > 0.4f) {
23 battery = Common::Input::BatteryLevel::Low;
24 }
25 if (value > 0.6f) {
26 battery = Common::Input::BatteryLevel::Medium;
27 }
28 if (value > 0.8f) {
29 battery = Common::Input::BatteryLevel::Full;
30 }
31 if (value >= 1.0f) {
32 battery = Common::Input::BatteryLevel::Charging;
33 }
34 break;
35 }
36 case Common::Input::InputType::Button:
37 battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging
38 : Common::Input::BatteryLevel::Critical;
39 break;
40 case Common::Input::InputType::Battery:
41 battery = callback.battery_status;
42 break;
43 default:
44 LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type);
45 break;
46 }
47
48 return battery;
49}
50
51Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) {
52 Common::Input::ButtonStatus status{};
53 switch (callback.type) {
54 case Common::Input::InputType::Analog:
55 case Common::Input::InputType::Trigger:
56 status.value = TransformToTrigger(callback).pressed.value;
57 break;
58 case Common::Input::InputType::Button:
59 status = callback.button_status;
60 break;
61 default:
62 LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
63 break;
64 }
65
66 if (status.inverted) {
67 status.value = !status.value;
68 }
69
70 return status;
71}
72
73Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) {
74 Common::Input::MotionStatus status{};
75 switch (callback.type) {
76 case Common::Input::InputType::Button: {
77 Common::Input::AnalogProperties properties{
78 .deadzone = 0.0f,
79 .range = 1.0f,
80 .offset = 0.0f,
81 };
82 status.delta_timestamp = 5000;
83 status.force_update = true;
84 status.accel.x = {
85 .value = 0.0f,
86 .raw_value = 0.0f,
87 .properties = properties,
88 };
89 status.accel.y = {
90 .value = 0.0f,
91 .raw_value = 0.0f,
92 .properties = properties,
93 };
94 status.accel.z = {
95 .value = 0.0f,
96 .raw_value = -1.0f,
97 .properties = properties,
98 };
99 status.gyro.x = {
100 .value = 0.0f,
101 .raw_value = 0.0f,
102 .properties = properties,
103 };
104 status.gyro.y = {
105 .value = 0.0f,
106 .raw_value = 0.0f,
107 .properties = properties,
108 };
109 status.gyro.z = {
110 .value = 0.0f,
111 .raw_value = 0.0f,
112 .properties = properties,
113 };
114 if (TransformToButton(callback).value) {
115 std::random_device device;
116 std::mt19937 gen(device());
117 std::uniform_int_distribution<s16> distribution(-1000, 1000);
118 status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
119 status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
120 status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
121 status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
122 status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
123 status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
124 }
125 break;
126 }
127 case Common::Input::InputType::Motion:
128 status = callback.motion_status;
129 break;
130 default:
131 LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type);
132 break;
133 }
134 SanitizeAnalog(status.accel.x, false);
135 SanitizeAnalog(status.accel.y, false);
136 SanitizeAnalog(status.accel.z, false);
137 SanitizeAnalog(status.gyro.x, false);
138 SanitizeAnalog(status.gyro.y, false);
139 SanitizeAnalog(status.gyro.z, false);
140
141 return status;
142}
143
144Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) {
145 Common::Input::StickStatus status{};
146
147 switch (callback.type) {
148 case Common::Input::InputType::Stick:
149 status = callback.stick_status;
150 break;
151 default:
152 LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type);
153 break;
154 }
155
156 SanitizeStick(status.x, status.y, true);
157 const auto& properties_x = status.x.properties;
158 const auto& properties_y = status.y.properties;
159 const float x = status.x.value;
160 const float y = status.y.value;
161
162 // Set directional buttons
163 status.right = x > properties_x.threshold;
164 status.left = x < -properties_x.threshold;
165 status.up = y > properties_y.threshold;
166 status.down = y < -properties_y.threshold;
167
168 return status;
169}
170
171Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) {
172 Common::Input::TouchStatus status{};
173
174 switch (callback.type) {
175 case Common::Input::InputType::Touch:
176 status = callback.touch_status;
177 break;
178 case Common::Input::InputType::Stick:
179 status.x = callback.stick_status.x;
180 status.y = callback.stick_status.y;
181 break;
182 default:
183 LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type);
184 break;
185 }
186
187 SanitizeAnalog(status.x, true);
188 SanitizeAnalog(status.y, true);
189 float& x = status.x.value;
190 float& y = status.y.value;
191
192 // Adjust if value is inverted
193 x = status.x.properties.inverted ? 1.0f + x : x;
194 y = status.y.properties.inverted ? 1.0f + y : y;
195
196 // clamp value
197 x = std::clamp(x, 0.0f, 1.0f);
198 y = std::clamp(y, 0.0f, 1.0f);
199
200 if (status.pressed.inverted) {
201 status.pressed.value = !status.pressed.value;
202 }
203
204 return status;
205}
206
207Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) {
208 Common::Input::TriggerStatus status{};
209 float& raw_value = status.analog.raw_value;
210 bool calculate_button_value = true;
211
212 switch (callback.type) {
213 case Common::Input::InputType::Analog:
214 status.analog.properties = callback.analog_status.properties;
215 raw_value = callback.analog_status.raw_value;
216 break;
217 case Common::Input::InputType::Button:
218 status.analog.properties.range = 1.0f;
219 status.analog.properties.inverted = callback.button_status.inverted;
220 raw_value = callback.button_status.value ? 1.0f : 0.0f;
221 break;
222 case Common::Input::InputType::Trigger:
223 status = callback.trigger_status;
224 calculate_button_value = false;
225 break;
226 default:
227 LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
228 break;
229 }
230
231 SanitizeAnalog(status.analog, true);
232 const auto& properties = status.analog.properties;
233 float& value = status.analog.value;
234
235 // Set button status
236 if (calculate_button_value) {
237 status.pressed.value = value > properties.threshold;
238 }
239
240 // Adjust if value is inverted
241 value = properties.inverted ? 1.0f + value : value;
242
243 // clamp value
244 value = std::clamp(value, 0.0f, 1.0f);
245
246 return status;
247}
248
249Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) {
250 Common::Input::AnalogStatus status{};
251
252 switch (callback.type) {
253 case Common::Input::InputType::Analog:
254 status.properties = callback.analog_status.properties;
255 status.raw_value = callback.analog_status.raw_value;
256 break;
257 default:
258 LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type);
259 break;
260 }
261
262 SanitizeAnalog(status, false);
263
264 // Adjust if value is inverted
265 status.value = status.properties.inverted ? -status.value : status.value;
266
267 return status;
268}
269
270void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
271 const auto& properties = analog.properties;
272 float& raw_value = analog.raw_value;
273 float& value = analog.value;
274
275 if (!std::isnormal(raw_value)) {
276 raw_value = 0;
277 }
278
279 // Apply center offset
280 raw_value -= properties.offset;
281
282 // Set initial values to be formated
283 value = raw_value;
284
285 // Calculate vector size
286 const float r = std::abs(value);
287
288 // Return zero if value is smaller than the deadzone
289 if (r <= properties.deadzone || properties.deadzone == 1.0f) {
290 analog.value = 0;
291 return;
292 }
293
294 // Adjust range of value
295 const float deadzone_factor =
296 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone);
297 value = value * deadzone_factor / properties.range;
298
299 // Invert direction if needed
300 if (properties.inverted) {
301 value = -value;
302 }
303
304 // Clamp value
305 if (clamp_value) {
306 value = std::clamp(value, -1.0f, 1.0f);
307 }
308}
309
310void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
311 bool clamp_value) {
312 const auto& properties_x = analog_x.properties;
313 const auto& properties_y = analog_y.properties;
314 float& raw_x = analog_x.raw_value;
315 float& raw_y = analog_y.raw_value;
316 float& x = analog_x.value;
317 float& y = analog_y.value;
318
319 if (!std::isnormal(raw_x)) {
320 raw_x = 0;
321 }
322 if (!std::isnormal(raw_y)) {
323 raw_y = 0;
324 }
325
326 // Apply center offset
327 raw_x += properties_x.offset;
328 raw_y += properties_y.offset;
329
330 // Apply X scale correction from offset
331 if (std::abs(properties_x.offset) < 0.5f) {
332 if (raw_x > 0) {
333 raw_x /= 1 + properties_x.offset;
334 } else {
335 raw_x /= 1 - properties_x.offset;
336 }
337 }
338
339 // Apply Y scale correction from offset
340 if (std::abs(properties_y.offset) < 0.5f) {
341 if (raw_y > 0) {
342 raw_y /= 1 + properties_y.offset;
343 } else {
344 raw_y /= 1 - properties_y.offset;
345 }
346 }
347
348 // Invert direction if needed
349 raw_x = properties_x.inverted ? -raw_x : raw_x;
350 raw_y = properties_y.inverted ? -raw_y : raw_y;
351
352 // Set initial values to be formated
353 x = raw_x;
354 y = raw_y;
355
356 // Calculate vector size
357 float r = x * x + y * y;
358 r = std::sqrt(r);
359
360 // TODO(German77): Use deadzone and range of both axis
361
362 // Return zero if values are smaller than the deadzone
363 if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) {
364 x = 0;
365 y = 0;
366 return;
367 }
368
369 // Adjust range of joystick
370 const float deadzone_factor =
371 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone);
372 x = x * deadzone_factor / properties_x.range;
373 y = y * deadzone_factor / properties_x.range;
374 r = r * deadzone_factor / properties_x.range;
375
376 // Normalize joystick
377 if (clamp_value && r > 1.0f) {
378 x /= r;
379 y /= r;
380 }
381}
382
383} // namespace Core::HID
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
new file mode 100644
index 000000000..d24582226
--- /dev/null
+++ b/src/core/hid/input_converter.h
@@ -0,0 +1,96 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7namespace Common::Input {
8struct CallbackStatus;
9enum class BatteryLevel : u32;
10using BatteryStatus = BatteryLevel;
11struct AnalogStatus;
12struct ButtonStatus;
13struct MotionStatus;
14struct StickStatus;
15struct TouchStatus;
16struct TriggerStatus;
17}; // namespace Common::Input
18
19namespace Core::HID {
20
21/**
22 * Converts raw input data into a valid battery status.
23 *
24 * @param callback Supported callbacks: Analog, Battery, Trigger.
25 * @return A valid BatteryStatus object.
26 */
27Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback);
28
29/**
30 * Converts raw input data into a valid button status. Applies invert properties to the output.
31 *
32 * @param callback Supported callbacks: Analog, Button, Trigger.
33 * @return A valid TouchStatus object.
34 */
35Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback);
36
37/**
38 * Converts raw input data into a valid motion status.
39 *
40 * @param callback Supported callbacks: Motion.
41 * @return A valid TouchStatus object.
42 */
43Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback);
44
45/**
46 * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert
47 * properties to the output.
48 *
49 * @param callback Supported callbacks: Stick.
50 * @return A valid StickStatus object.
51 */
52Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback);
53
54/**
55 * Converts raw input data into a valid touch status.
56 *
57 * @param callback Supported callbacks: Touch.
58 * @return A valid TouchStatus object.
59 */
60Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback);
61
62/**
63 * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and
64 * invert properties to the output. Button status uses the threshold property if necessary.
65 *
66 * @param callback Supported callbacks: Analog, Button, Trigger.
67 * @return A valid TriggerStatus object.
68 */
69Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback);
70
71/**
72 * Converts raw input data into a valid analog status. Applies offset, deadzone, range and
73 * invert properties to the output.
74 *
75 * @param callback Supported callbacks: Analog.
76 * @return A valid AnalogStatus object.
77 */
78Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
79
80/**
81 * Converts raw analog data into a valid analog value
82 * @param analog An analog object containing raw data and properties
83 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
84 */
85void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value);
86
87/**
88 * Converts raw stick data into a valid stick value
89 * @param analog_x raw analog data and properties for the x-axis
90 * @param analog_y raw analog data and properties for the y-axis
91 * @param clamp_value bool that determines if the value needs to be clamped into the unit circle.
92 */
93void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
94 bool clamp_value);
95
96} // namespace Core::HID
diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 9f6a90e8f..2dbda8814 100644
--- a/src/core/frontend/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -3,7 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/core.h"
6#include "core/frontend/input_interpreter.h" 6#include "core/hid/hid_types.h"
7#include "core/hid/input_interpreter.h"
7#include "core/hle/service/hid/controllers/npad.h" 8#include "core/hle/service/hid/controllers/npad.h"
8#include "core/hle/service/hid/hid.h" 9#include "core/hle/service/hid/hid.h"
9#include "core/hle/service/sm/sm.h" 10#include "core/hle/service/sm/sm.h"
@@ -19,7 +20,7 @@ InputInterpreter::InputInterpreter(Core::System& system)
19InputInterpreter::~InputInterpreter() = default; 20InputInterpreter::~InputInterpreter() = default;
20 21
21void InputInterpreter::PollInput() { 22void InputInterpreter::PollInput() {
22 const u32 button_state = npad.GetAndResetPressState(); 23 const auto button_state = npad.GetAndResetPressState();
23 24
24 previous_index = current_index; 25 previous_index = current_index;
25 current_index = (current_index + 1) % button_states.size(); 26 current_index = (current_index + 1) % button_states.size();
@@ -31,32 +32,30 @@ void InputInterpreter::ResetButtonStates() {
31 previous_index = 0; 32 previous_index = 0;
32 current_index = 0; 33 current_index = 0;
33 34
34 button_states[0] = 0xFFFFFFFF; 35 button_states[0] = Core::HID::NpadButton::All;
35 36
36 for (std::size_t i = 1; i < button_states.size(); ++i) { 37 for (std::size_t i = 1; i < button_states.size(); ++i) {
37 button_states[i] = 0; 38 button_states[i] = Core::HID::NpadButton::None;
38 } 39 }
39} 40}
40 41
41bool InputInterpreter::IsButtonPressed(HIDButton button) const { 42bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
42 return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; 43 return True(button_states[current_index] & button);
43} 44}
44 45
45bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const { 46bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
46 const bool current_press = 47 const bool current_press = True(button_states[current_index] & button);
47 (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; 48 const bool previous_press = True(button_states[previous_index] & button);
48 const bool previous_press =
49 (button_states[previous_index] & (1U << static_cast<u8>(button))) != 0;
50 49
51 return current_press && !previous_press; 50 return current_press && !previous_press;
52} 51}
53 52
54bool InputInterpreter::IsButtonHeld(HIDButton button) const { 53bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
55 u32 held_buttons{button_states[0]}; 54 Core::HID::NpadButton held_buttons{button_states[0]};
56 55
57 for (std::size_t i = 1; i < button_states.size(); ++i) { 56 for (std::size_t i = 1; i < button_states.size(); ++i) {
58 held_buttons &= button_states[i]; 57 held_buttons &= button_states[i];
59 } 58 }
60 59
61 return (held_buttons & (1U << static_cast<u8>(button))) != 0; 60 return True(held_buttons & button);
62} 61}
diff --git a/src/core/frontend/input_interpreter.h b/src/core/hid/input_interpreter.h
index 9495e3daf..70c34d474 100644
--- a/src/core/frontend/input_interpreter.h
+++ b/src/core/hid/input_interpreter.h
@@ -12,46 +12,14 @@ namespace Core {
12class System; 12class System;
13} 13}
14 14
15namespace Core::HID {
16enum class NpadButton : u64;
17}
18
15namespace Service::HID { 19namespace Service::HID {
16class Controller_NPad; 20class Controller_NPad;
17} 21}
18 22
19enum class HIDButton : u8 {
20 A,
21 B,
22 X,
23 Y,
24 LStick,
25 RStick,
26 L,
27 R,
28 ZL,
29 ZR,
30 Plus,
31 Minus,
32
33 DLeft,
34 DUp,
35 DRight,
36 DDown,
37
38 LStickLeft,
39 LStickUp,
40 LStickRight,
41 LStickDown,
42
43 RStickLeft,
44 RStickUp,
45 RStickRight,
46 RStickDown,
47
48 LeftSL,
49 LeftSR,
50
51 RightSL,
52 RightSR,
53};
54
55/** 23/**
56 * The InputInterpreter class interfaces with HID to retrieve button press states. 24 * The InputInterpreter class interfaces with HID to retrieve button press states.
57 * Input is intended to be polled every 50ms so that a button is considered to be 25 * Input is intended to be polled every 50ms so that a button is considered to be
@@ -76,7 +44,7 @@ public:
76 * 44 *
77 * @returns True when the button is pressed. 45 * @returns True when the button is pressed.
78 */ 46 */
79 [[nodiscard]] bool IsButtonPressed(HIDButton button) const; 47 [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const;
80 48
81 /** 49 /**
82 * Checks whether any of the buttons in the parameter list is pressed. 50 * Checks whether any of the buttons in the parameter list is pressed.
@@ -85,7 +53,7 @@ public:
85 * 53 *
86 * @returns True when at least one of the buttons is pressed. 54 * @returns True when at least one of the buttons is pressed.
87 */ 55 */
88 template <HIDButton... T> 56 template <Core::HID::NpadButton... T>
89 [[nodiscard]] bool IsAnyButtonPressed() { 57 [[nodiscard]] bool IsAnyButtonPressed() {
90 return (IsButtonPressed(T) || ...); 58 return (IsButtonPressed(T) || ...);
91 } 59 }
@@ -98,7 +66,7 @@ public:
98 * 66 *
99 * @returns True when the button is pressed once. 67 * @returns True when the button is pressed once.
100 */ 68 */
101 [[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const; 69 [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const;
102 70
103 /** 71 /**
104 * Checks whether any of the buttons in the parameter list is pressed once. 72 * Checks whether any of the buttons in the parameter list is pressed once.
@@ -107,7 +75,7 @@ public:
107 * 75 *
108 * @returns True when at least one of the buttons is pressed once. 76 * @returns True when at least one of the buttons is pressed once.
109 */ 77 */
110 template <HIDButton... T> 78 template <Core::HID::NpadButton... T>
111 [[nodiscard]] bool IsAnyButtonPressedOnce() const { 79 [[nodiscard]] bool IsAnyButtonPressedOnce() const {
112 return (IsButtonPressedOnce(T) || ...); 80 return (IsButtonPressedOnce(T) || ...);
113 } 81 }
@@ -119,7 +87,7 @@ public:
119 * 87 *
120 * @returns True when the button is held down. 88 * @returns True when the button is held down.
121 */ 89 */
122 [[nodiscard]] bool IsButtonHeld(HIDButton button) const; 90 [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const;
123 91
124 /** 92 /**
125 * Checks whether any of the buttons in the parameter list is held down. 93 * Checks whether any of the buttons in the parameter list is held down.
@@ -128,7 +96,7 @@ public:
128 * 96 *
129 * @returns True when at least one of the buttons is held down. 97 * @returns True when at least one of the buttons is held down.
130 */ 98 */
131 template <HIDButton... T> 99 template <Core::HID::NpadButton... T>
132 [[nodiscard]] bool IsAnyButtonHeld() const { 100 [[nodiscard]] bool IsAnyButtonHeld() const {
133 return (IsButtonHeld(T) || ...); 101 return (IsButtonHeld(T) || ...);
134 } 102 }
@@ -137,7 +105,7 @@ private:
137 Service::HID::Controller_NPad& npad; 105 Service::HID::Controller_NPad& npad;
138 106
139 /// Stores 9 consecutive button states polled from HID. 107 /// Stores 9 consecutive button states polled from HID.
140 std::array<u32, 9> button_states{}; 108 std::array<Core::HID::NpadButton, 9> button_states{};
141 109
142 std::size_t previous_index{}; 110 std::size_t previous_index{};
143 std::size_t current_index{}; 111 std::size_t current_index{};
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp
new file mode 100644
index 000000000..c25fea966
--- /dev/null
+++ b/src/core/hid/motion_input.cpp
@@ -0,0 +1,280 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/math_util.h"
6#include "core/hid/motion_input.h"
7
8namespace Core::HID {
9
10MotionInput::MotionInput() {
11 // Initialize PID constants with default values
12 SetPID(0.3f, 0.005f, 0.0f);
13}
14
15void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
16 kp = new_kp;
17 ki = new_ki;
18 kd = new_kd;
19}
20
21void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
22 accel = acceleration;
23}
24
25void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
26 gyro = gyroscope - gyro_drift;
27
28 // Auto adjust drift to minimize drift
29 if (!IsMoving(0.1f)) {
30 gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f);
31 }
32
33 if (gyro.Length2() < gyro_threshold) {
34 gyro = {};
35 } else {
36 only_accelerometer = false;
37 }
38}
39
40void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
41 quat = quaternion;
42}
43
44void MotionInput::SetGyroDrift(const Common::Vec3f& drift) {
45 gyro_drift = drift;
46}
47
48void MotionInput::SetGyroThreshold(f32 threshold) {
49 gyro_threshold = threshold;
50}
51
52void MotionInput::EnableReset(bool reset) {
53 reset_enabled = reset;
54}
55
56void MotionInput::ResetRotations() {
57 rotations = {};
58}
59
60bool MotionInput::IsMoving(f32 sensitivity) const {
61 return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f;
62}
63
64bool MotionInput::IsCalibrated(f32 sensitivity) const {
65 return real_error.Length() < sensitivity;
66}
67
68void MotionInput::UpdateRotation(u64 elapsed_time) {
69 const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
70 if (sample_period > 0.1f) {
71 return;
72 }
73 rotations += gyro * sample_period;
74}
75
76// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
77// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
78void MotionInput::UpdateOrientation(u64 elapsed_time) {
79 if (!IsCalibrated(0.1f)) {
80 ResetOrientation();
81 }
82 // Short name local variable for readability
83 f32 q1 = quat.w;
84 f32 q2 = quat.xyz[0];
85 f32 q3 = quat.xyz[1];
86 f32 q4 = quat.xyz[2];
87 const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
88
89 // Ignore invalid elapsed time
90 if (sample_period > 0.1f) {
91 return;
92 }
93
94 const auto normal_accel = accel.Normalized();
95 auto rad_gyro = gyro * Common::PI * 2;
96 const f32 swap = rad_gyro.x;
97 rad_gyro.x = rad_gyro.y;
98 rad_gyro.y = -swap;
99 rad_gyro.z = -rad_gyro.z;
100
101 // Clear gyro values if there is no gyro present
102 if (only_accelerometer) {
103 rad_gyro.x = 0;
104 rad_gyro.y = 0;
105 rad_gyro.z = 0;
106 }
107
108 // Ignore drift correction if acceleration is not reliable
109 if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
110 const f32 ax = -normal_accel.x;
111 const f32 ay = normal_accel.y;
112 const f32 az = -normal_accel.z;
113
114 // Estimated direction of gravity
115 const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
116 const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
117 const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
118
119 // Error is cross product between estimated direction and measured direction of gravity
120 const Common::Vec3f new_real_error = {
121 az * vx - ax * vz,
122 ay * vz - az * vy,
123 ax * vy - ay * vx,
124 };
125
126 derivative_error = new_real_error - real_error;
127 real_error = new_real_error;
128
129 // Prevent integral windup
130 if (ki != 0.0f && !IsCalibrated(0.05f)) {
131 integral_error += real_error;
132 } else {
133 integral_error = {};
134 }
135
136 // Apply feedback terms
137 if (!only_accelerometer) {
138 rad_gyro += kp * real_error;
139 rad_gyro += ki * integral_error;
140 rad_gyro += kd * derivative_error;
141 } else {
142 // Give more weight to accelerometer values to compensate for the lack of gyro
143 rad_gyro += 35.0f * kp * real_error;
144 rad_gyro += 10.0f * ki * integral_error;
145 rad_gyro += 10.0f * kd * derivative_error;
146
147 // Emulate gyro values for games that need them
148 gyro.x = -rad_gyro.y;
149 gyro.y = rad_gyro.x;
150 gyro.z = -rad_gyro.z;
151 UpdateRotation(elapsed_time);
152 }
153 }
154
155 const f32 gx = rad_gyro.y;
156 const f32 gy = rad_gyro.x;
157 const f32 gz = rad_gyro.z;
158
159 // Integrate rate of change of quaternion
160 const f32 pa = q2;
161 const f32 pb = q3;
162 const f32 pc = q4;
163 q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
164 q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
165 q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
166 q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
167
168 quat.w = q1;
169 quat.xyz[0] = q2;
170 quat.xyz[1] = q3;
171 quat.xyz[2] = q4;
172 quat = quat.Normalized();
173}
174
175std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const {
176 const Common::Quaternion<float> quad{
177 .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w},
178 .w = -quat.xyz[2],
179 };
180 const std::array<float, 16> matrix4x4 = quad.ToMatrix();
181
182 return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
183 Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
184 Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
185}
186
187Common::Vec3f MotionInput::GetAcceleration() const {
188 return accel;
189}
190
191Common::Vec3f MotionInput::GetGyroscope() const {
192 return gyro;
193}
194
195Common::Quaternion<f32> MotionInput::GetQuaternion() const {
196 return quat;
197}
198
199Common::Vec3f MotionInput::GetRotations() const {
200 return rotations;
201}
202
203void MotionInput::ResetOrientation() {
204 if (!reset_enabled || only_accelerometer) {
205 return;
206 }
207 if (!IsMoving(0.5f) && accel.z <= -0.9f) {
208 ++reset_counter;
209 if (reset_counter > 900) {
210 quat.w = 0;
211 quat.xyz[0] = 0;
212 quat.xyz[1] = 0;
213 quat.xyz[2] = -1;
214 SetOrientationFromAccelerometer();
215 integral_error = {};
216 reset_counter = 0;
217 }
218 } else {
219 reset_counter = 0;
220 }
221}
222
223void MotionInput::SetOrientationFromAccelerometer() {
224 int iterations = 0;
225 const f32 sample_period = 0.015f;
226
227 const auto normal_accel = accel.Normalized();
228
229 while (!IsCalibrated(0.01f) && ++iterations < 100) {
230 // Short name local variable for readability
231 f32 q1 = quat.w;
232 f32 q2 = quat.xyz[0];
233 f32 q3 = quat.xyz[1];
234 f32 q4 = quat.xyz[2];
235
236 Common::Vec3f rad_gyro;
237 const f32 ax = -normal_accel.x;
238 const f32 ay = normal_accel.y;
239 const f32 az = -normal_accel.z;
240
241 // Estimated direction of gravity
242 const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
243 const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
244 const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
245
246 // Error is cross product between estimated direction and measured direction of gravity
247 const Common::Vec3f new_real_error = {
248 az * vx - ax * vz,
249 ay * vz - az * vy,
250 ax * vy - ay * vx,
251 };
252
253 derivative_error = new_real_error - real_error;
254 real_error = new_real_error;
255
256 rad_gyro += 10.0f * kp * real_error;
257 rad_gyro += 5.0f * ki * integral_error;
258 rad_gyro += 10.0f * kd * derivative_error;
259
260 const f32 gx = rad_gyro.y;
261 const f32 gy = rad_gyro.x;
262 const f32 gz = rad_gyro.z;
263
264 // Integrate rate of change of quaternion
265 const f32 pa = q2;
266 const f32 pb = q3;
267 const f32 pc = q4;
268 q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
269 q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
270 q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
271 q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
272
273 quat.w = q1;
274 quat.xyz[0] = q2;
275 quat.xyz[1] = q3;
276 quat.xyz[2] = q4;
277 quat = quat.Normalized();
278 }
279}
280} // namespace Core::HID
diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h
new file mode 100644
index 000000000..5b5b420bb
--- /dev/null
+++ b/src/core/hid/motion_input.h
@@ -0,0 +1,87 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/quaternion.h"
9#include "common/vector_math.h"
10
11namespace Core::HID {
12
13class MotionInput {
14public:
15 explicit MotionInput();
16
17 MotionInput(const MotionInput&) = default;
18 MotionInput& operator=(const MotionInput&) = default;
19
20 MotionInput(MotionInput&&) = default;
21 MotionInput& operator=(MotionInput&&) = default;
22
23 void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
24 void SetAcceleration(const Common::Vec3f& acceleration);
25 void SetGyroscope(const Common::Vec3f& gyroscope);
26 void SetQuaternion(const Common::Quaternion<f32>& quaternion);
27 void SetGyroDrift(const Common::Vec3f& drift);
28 void SetGyroThreshold(f32 threshold);
29
30 void EnableReset(bool reset);
31 void ResetRotations();
32
33 void UpdateRotation(u64 elapsed_time);
34 void UpdateOrientation(u64 elapsed_time);
35
36 [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
37 [[nodiscard]] Common::Vec3f GetAcceleration() const;
38 [[nodiscard]] Common::Vec3f GetGyroscope() const;
39 [[nodiscard]] Common::Vec3f GetRotations() const;
40 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
41
42 [[nodiscard]] bool IsMoving(f32 sensitivity) const;
43 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
44
45private:
46 void ResetOrientation();
47 void SetOrientationFromAccelerometer();
48
49 // PID constants
50 f32 kp;
51 f32 ki;
52 f32 kd;
53
54 // PID errors
55 Common::Vec3f real_error;
56 Common::Vec3f integral_error;
57 Common::Vec3f derivative_error;
58
59 // Quaternion containing the device orientation
60 Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f};
61
62 // Number of full rotations in each axis
63 Common::Vec3f rotations;
64
65 // Acceleration vector measurement in G force
66 Common::Vec3f accel;
67
68 // Gyroscope vector measurement in radians/s.
69 Common::Vec3f gyro;
70
71 // Vector to be substracted from gyro measurements
72 Common::Vec3f gyro_drift;
73
74 // Minimum gyro amplitude to detect if the device is moving
75 f32 gyro_threshold = 0.0f;
76
77 // Number of invalid sequential data
78 u32 reset_counter = 0;
79
80 // If the provided data is invalid the device will be autocalibrated
81 bool reset_enabled = true;
82
83 // Use accelerometer values to calculate position
84 bool only_accelerometer = true;
85};
86
87} // namespace Core::HID
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 8ff0f695d..36fc0944a 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -9,6 +9,7 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hardware_properties.h" 10#include "core/hardware_properties.h"
11#include "core/hle/kernel/init/init_slab_setup.h" 11#include "core/hle/kernel/init/init_slab_setup.h"
12#include "core/hle/kernel/k_code_memory.h"
12#include "core/hle/kernel/k_event.h" 13#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_memory_layout.h" 14#include "core/hle/kernel/k_memory_layout.h"
14#include "core/hle/kernel/k_memory_manager.h" 15#include "core/hle/kernel/k_memory_manager.h"
@@ -32,6 +33,7 @@ namespace Kernel::Init {
32 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ 33 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
33 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ 34 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
34 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ 35 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
36 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
35 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ 37 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
36 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) 38 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
37 39
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 1b429bc1e..783c69858 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -8,6 +8,7 @@
8#include "core/hle/kernel/k_scheduler.h" 8#include "core/hle/kernel/k_scheduler.h"
9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
10#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
11#include "core/hle/kernel/k_thread_queue.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/svc_results.h" 13#include "core/hle/kernel/svc_results.h"
13#include "core/hle/kernel/time_manager.h" 14#include "core/hle/kernel/time_manager.h"
@@ -28,7 +29,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
28 29
29bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { 30bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
30 auto& monitor = system.Monitor(); 31 auto& monitor = system.Monitor();
31 const auto current_core = system.CurrentCoreIndex(); 32 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
32 33
33 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 34 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
34 // TODO(bunnei): We should call CanAccessAtomic(..) here. 35 // TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -58,7 +59,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
58 59
59bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { 60bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
60 auto& monitor = system.Monitor(); 61 auto& monitor = system.Monitor();
61 const auto current_core = system.CurrentCoreIndex(); 62 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
62 63
63 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 64 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
64 // TODO(bunnei): We should call CanAccessAtomic(..) here. 65 // TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -85,6 +86,27 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
85 return true; 86 return true;
86} 87}
87 88
89class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
90public:
91 explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
92 : KThreadQueue(kernel_), m_tree(t) {}
93
94 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
95 bool cancel_timer_task) override {
96 // If the thread is waiting on an address arbiter, remove it from the tree.
97 if (waiting_thread->IsWaitingForAddressArbiter()) {
98 m_tree->erase(m_tree->iterator_to(*waiting_thread));
99 waiting_thread->ClearAddressArbiter();
100 }
101
102 // Invoke the base cancel wait handler.
103 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
104 }
105
106private:
107 KAddressArbiter::ThreadTree* m_tree;
108};
109
88} // namespace 110} // namespace
89 111
90ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { 112ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
@@ -96,14 +118,14 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
96 auto it = thread_tree.nfind_light({addr, -1}); 118 auto it = thread_tree.nfind_light({addr, -1});
97 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 119 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
98 (it->GetAddressArbiterKey() == addr)) { 120 (it->GetAddressArbiterKey() == addr)) {
121 // End the thread's wait.
99 KThread* target_thread = std::addressof(*it); 122 KThread* target_thread = std::addressof(*it);
100 target_thread->SetSyncedObject(nullptr, ResultSuccess); 123 target_thread->EndWait(ResultSuccess);
101 124
102 ASSERT(target_thread->IsWaitingForAddressArbiter()); 125 ASSERT(target_thread->IsWaitingForAddressArbiter());
103 target_thread->Wakeup(); 126 target_thread->ClearAddressArbiter();
104 127
105 it = thread_tree.erase(it); 128 it = thread_tree.erase(it);
106 target_thread->ClearAddressArbiter();
107 ++num_waiters; 129 ++num_waiters;
108 } 130 }
109 } 131 }
@@ -129,14 +151,14 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
129 auto it = thread_tree.nfind_light({addr, -1}); 151 auto it = thread_tree.nfind_light({addr, -1});
130 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 152 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
131 (it->GetAddressArbiterKey() == addr)) { 153 (it->GetAddressArbiterKey() == addr)) {
154 // End the thread's wait.
132 KThread* target_thread = std::addressof(*it); 155 KThread* target_thread = std::addressof(*it);
133 target_thread->SetSyncedObject(nullptr, ResultSuccess); 156 target_thread->EndWait(ResultSuccess);
134 157
135 ASSERT(target_thread->IsWaitingForAddressArbiter()); 158 ASSERT(target_thread->IsWaitingForAddressArbiter());
136 target_thread->Wakeup(); 159 target_thread->ClearAddressArbiter();
137 160
138 it = thread_tree.erase(it); 161 it = thread_tree.erase(it);
139 target_thread->ClearAddressArbiter();
140 ++num_waiters; 162 ++num_waiters;
141 } 163 }
142 } 164 }
@@ -197,14 +219,14 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
197 219
198 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 220 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
199 (it->GetAddressArbiterKey() == addr)) { 221 (it->GetAddressArbiterKey() == addr)) {
222 // End the thread's wait.
200 KThread* target_thread = std::addressof(*it); 223 KThread* target_thread = std::addressof(*it);
201 target_thread->SetSyncedObject(nullptr, ResultSuccess); 224 target_thread->EndWait(ResultSuccess);
202 225
203 ASSERT(target_thread->IsWaitingForAddressArbiter()); 226 ASSERT(target_thread->IsWaitingForAddressArbiter());
204 target_thread->Wakeup(); 227 target_thread->ClearAddressArbiter();
205 228
206 it = thread_tree.erase(it); 229 it = thread_tree.erase(it);
207 target_thread->ClearAddressArbiter();
208 ++num_waiters; 230 ++num_waiters;
209 } 231 }
210 } 232 }
@@ -214,6 +236,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
214ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { 236ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
215 // Prepare to wait. 237 // Prepare to wait.
216 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); 238 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
239 ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
217 240
218 { 241 {
219 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; 242 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -224,9 +247,6 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
224 return ResultTerminationRequested; 247 return ResultTerminationRequested;
225 } 248 }
226 249
227 // Set the synced object.
228 cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
229
230 // Read the value from userspace. 250 // Read the value from userspace.
231 s32 user_value{}; 251 s32 user_value{};
232 bool succeeded{}; 252 bool succeeded{};
@@ -256,31 +276,20 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
256 // Set the arbiter. 276 // Set the arbiter.
257 cur_thread->SetAddressArbiter(&thread_tree, addr); 277 cur_thread->SetAddressArbiter(&thread_tree, addr);
258 thread_tree.insert(*cur_thread); 278 thread_tree.insert(*cur_thread);
259 cur_thread->SetState(ThreadState::Waiting);
260 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
261 }
262
263 // Cancel the timer wait.
264 kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
265 279
266 // Remove from the address arbiter. 280 // Wait for the thread to finish.
267 { 281 cur_thread->BeginWait(std::addressof(wait_queue));
268 KScopedSchedulerLock sl(kernel); 282 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
269
270 if (cur_thread->IsWaitingForAddressArbiter()) {
271 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
272 cur_thread->ClearAddressArbiter();
273 }
274 } 283 }
275 284
276 // Get the result. 285 // Get the result.
277 KSynchronizationObject* dummy{}; 286 return cur_thread->GetWaitResult();
278 return cur_thread->GetWaitResult(&dummy);
279} 287}
280 288
281ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { 289ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
282 // Prepare to wait. 290 // Prepare to wait.
283 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); 291 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
292 ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
284 293
285 { 294 {
286 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; 295 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -291,9 +300,6 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
291 return ResultTerminationRequested; 300 return ResultTerminationRequested;
292 } 301 }
293 302
294 // Set the synced object.
295 cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
296
297 // Read the value from userspace. 303 // Read the value from userspace.
298 s32 user_value{}; 304 s32 user_value{};
299 if (!ReadFromUser(system, &user_value, addr)) { 305 if (!ReadFromUser(system, &user_value, addr)) {
@@ -316,26 +322,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
316 // Set the arbiter. 322 // Set the arbiter.
317 cur_thread->SetAddressArbiter(&thread_tree, addr); 323 cur_thread->SetAddressArbiter(&thread_tree, addr);
318 thread_tree.insert(*cur_thread); 324 thread_tree.insert(*cur_thread);
319 cur_thread->SetState(ThreadState::Waiting);
320 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
321 }
322
323 // Cancel the timer wait.
324 kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
325 325
326 // Remove from the address arbiter. 326 // Wait for the thread to finish.
327 { 327 cur_thread->BeginWait(std::addressof(wait_queue));
328 KScopedSchedulerLock sl(kernel); 328 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
329
330 if (cur_thread->IsWaitingForAddressArbiter()) {
331 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
332 cur_thread->ClearAddressArbiter();
333 }
334 } 329 }
335 330
336 // Get the result. 331 // Get the result.
337 KSynchronizationObject* dummy{}; 332 return cur_thread->GetWaitResult();
338 return cur_thread->GetWaitResult(&dummy);
339} 333}
340 334
341} // namespace Kernel 335} // namespace Kernel
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index e4fcdbc67..165b76747 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -170,6 +170,10 @@ public:
170 } 170 }
171 } 171 }
172 172
173 const std::string& GetName() const {
174 return name;
175 }
176
173private: 177private:
174 void RegisterWithKernel(); 178 void RegisterWithKernel();
175 void UnregisterWithKernel(); 179 void UnregisterWithKernel();
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
index 0be0027be..21e2fe494 100644
--- a/src/core/hle/kernel/k_class_token.cpp
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -6,6 +6,7 @@
6#include "core/hle/kernel/k_class_token.h" 6#include "core/hle/kernel/k_class_token.h"
7#include "core/hle/kernel/k_client_port.h" 7#include "core/hle/kernel/k_client_port.h"
8#include "core/hle/kernel/k_client_session.h" 8#include "core/hle/kernel/k_client_session.h"
9#include "core/hle/kernel/k_code_memory.h"
9#include "core/hle/kernel/k_event.h" 10#include "core/hle/kernel/k_event.h"
10#include "core/hle/kernel/k_port.h" 11#include "core/hle/kernel/k_port.h"
11#include "core/hle/kernel/k_process.h" 12#include "core/hle/kernel/k_process.h"
@@ -48,7 +49,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
48static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000); 49static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
49// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000); 50// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
50// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000); 51// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
51// static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000); 52static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
52 53
53// Ensure that the token hierarchy is correct. 54// Ensure that the token hierarchy is correct.
54 55
@@ -79,7 +80,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut
79static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>)); 80static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
80// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>)); 81// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
81// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>)); 82// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
82// static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>)); 83static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
83 84
84// Ensure that the token hierarchy reflects the class hierarchy. 85// Ensure that the token hierarchy reflects the class hierarchy.
85 86
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
new file mode 100644
index 000000000..d69f7ffb7
--- /dev/null
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -0,0 +1,146 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "core/device_memory.h"
7#include "core/hle/kernel/k_auto_object.h"
8#include "core/hle/kernel/k_code_memory.h"
9#include "core/hle/kernel/k_light_lock.h"
10#include "core/hle/kernel/k_memory_block.h"
11#include "core/hle/kernel/k_page_linked_list.h"
12#include "core/hle/kernel/k_page_table.h"
13#include "core/hle/kernel/k_process.h"
14#include "core/hle/kernel/slab_helpers.h"
15#include "core/hle/kernel/svc_types.h"
16#include "core/hle/result.h"
17
18namespace Kernel {
19
20KCodeMemory::KCodeMemory(KernelCore& kernel_)
21 : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
22
23ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
24 // Set members.
25 m_owner = kernel.CurrentProcess();
26
27 // Get the owner page table.
28 auto& page_table = m_owner->PageTable();
29
30 // Construct the page group.
31 KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
32 m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
33
34 // Lock the memory.
35 R_TRY(page_table.LockForCodeMemory(addr, size))
36
37 // Clear the memory.
38 for (const auto& block : m_page_group.Nodes()) {
39 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
40 }
41
42 // Set remaining tracking members.
43 m_address = addr;
44 m_is_initialized = true;
45 m_is_owner_mapped = false;
46 m_is_mapped = false;
47
48 // We succeeded.
49 return ResultSuccess;
50}
51
52void KCodeMemory::Finalize() {
53 // Unlock.
54 if (!m_is_mapped && !m_is_owner_mapped) {
55 const size_t size = m_page_group.GetNumPages() * PageSize;
56 m_owner->PageTable().UnlockForCodeMemory(m_address, size);
57 }
58}
59
60ResultCode KCodeMemory::Map(VAddr address, size_t size) {
61 // Validate the size.
62 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
63
64 // Lock ourselves.
65 KScopedLightLock lk(m_lock);
66
67 // Ensure we're not already mapped.
68 R_UNLESS(!m_is_mapped, ResultInvalidState);
69
70 // Map the memory.
71 R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
72 address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
73
74 // Mark ourselves as mapped.
75 m_is_mapped = true;
76
77 return ResultSuccess;
78}
79
80ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
81 // Validate the size.
82 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
83
84 // Lock ourselves.
85 KScopedLightLock lk(m_lock);
86
87 // Unmap the memory.
88 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
89 KMemoryState::CodeOut));
90
91 // Mark ourselves as unmapped.
92 m_is_mapped = false;
93
94 return ResultSuccess;
95}
96
97ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
98 // Validate the size.
99 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
100
101 // Lock ourselves.
102 KScopedLightLock lk(m_lock);
103
104 // Ensure we're not already mapped.
105 R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
106
107 // Convert the memory permission.
108 KMemoryPermission k_perm{};
109 switch (perm) {
110 case Svc::MemoryPermission::Read:
111 k_perm = KMemoryPermission::UserRead;
112 break;
113 case Svc::MemoryPermission::ReadExecute:
114 k_perm = KMemoryPermission::UserReadExecute;
115 break;
116 default:
117 break;
118 }
119
120 // Map the memory.
121 R_TRY(
122 m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
123
124 // Mark ourselves as mapped.
125 m_is_owner_mapped = true;
126
127 return ResultSuccess;
128}
129
130ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
131 // Validate the size.
132 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
133
134 // Lock ourselves.
135 KScopedLightLock lk(m_lock);
136
137 // Unmap the memory.
138 R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
139
140 // Mark ourselves as unmapped.
141 m_is_owner_mapped = false;
142
143 return ResultSuccess;
144}
145
146} // namespace Kernel \ No newline at end of file
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
new file mode 100644
index 000000000..e0ba19a53
--- /dev/null
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -0,0 +1,66 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/device_memory.h"
9#include "core/hle/kernel/k_auto_object.h"
10#include "core/hle/kernel/k_light_lock.h"
11#include "core/hle/kernel/k_page_linked_list.h"
12#include "core/hle/kernel/k_process.h"
13#include "core/hle/kernel/slab_helpers.h"
14#include "core/hle/kernel/svc_types.h"
15#include "core/hle/result.h"
16
17namespace Kernel {
18
19enum class CodeMemoryOperation : u32 {
20 Map = 0,
21 MapToOwner = 1,
22 Unmap = 2,
23 UnmapFromOwner = 3,
24};
25
26class KCodeMemory final
27 : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
28 KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
29
30public:
31 explicit KCodeMemory(KernelCore& kernel_);
32
33 ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
34 void Finalize();
35
36 ResultCode Map(VAddr address, size_t size);
37 ResultCode Unmap(VAddr address, size_t size);
38 ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
39 ResultCode UnmapFromOwner(VAddr address, size_t size);
40
41 bool IsInitialized() const {
42 return m_is_initialized;
43 }
44 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
45
46 KProcess* GetOwner() const {
47 return m_owner;
48 }
49 VAddr GetSourceAddress() const {
50 return m_address;
51 }
52 size_t GetSize() const {
53 return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
54 }
55
56private:
57 KPageLinkedList m_page_group{};
58 KProcess* m_owner{};
59 VAddr m_address{};
60 KLightLock m_lock;
61 bool m_is_initialized{};
62 bool m_is_owner_mapped{};
63 bool m_is_mapped{};
64};
65
66} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 7fa9b8cc3..aadcc297a 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
12#include "core/hle/kernel/k_synchronization_object.h" 12#include "core/hle/kernel/k_synchronization_object.h"
13#include "core/hle/kernel/k_thread.h" 13#include "core/hle/kernel/k_thread.h"
14#include "core/hle/kernel/k_thread_queue.h"
14#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
15#include "core/hle/kernel/svc_common.h" 16#include "core/hle/kernel/svc_common.h"
16#include "core/hle/kernel/svc_results.h" 17#include "core/hle/kernel/svc_results.h"
@@ -33,7 +34,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
33bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, 34bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
34 u32 new_orr_mask) { 35 u32 new_orr_mask) {
35 auto& monitor = system.Monitor(); 36 auto& monitor = system.Monitor();
36 const auto current_core = system.CurrentCoreIndex(); 37 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
37 38
38 // Load the value from the address. 39 // Load the value from the address.
39 const auto expected = monitor.ExclusiveRead32(current_core, address); 40 const auto expected = monitor.ExclusiveRead32(current_core, address);
@@ -57,6 +58,48 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero
57 return true; 58 return true;
58} 59}
59 60
61class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
62public:
63 explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
64 : KThreadQueue(kernel_) {}
65
66 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
67 bool cancel_timer_task) override {
68 // Remove the thread as a waiter from its owner.
69 waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
70
71 // Invoke the base cancel wait handler.
72 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
73 }
74};
75
76class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
77private:
78 KConditionVariable::ThreadTree* m_tree;
79
80public:
81 explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
82 KernelCore& kernel_, KConditionVariable::ThreadTree* t)
83 : KThreadQueue(kernel_), m_tree(t) {}
84
85 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
86 bool cancel_timer_task) override {
87 // Remove the thread as a waiter from its owner.
88 if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
89 owner->RemoveWaiter(waiting_thread);
90 }
91
92 // If the thread is waiting on a condvar, remove it from the tree.
93 if (waiting_thread->IsWaitingForConditionVariable()) {
94 m_tree->erase(m_tree->iterator_to(*waiting_thread));
95 waiting_thread->ClearConditionVariable();
96 }
97
98 // Invoke the base cancel wait handler.
99 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
100 }
101};
102
60} // namespace 103} // namespace
61 104
62KConditionVariable::KConditionVariable(Core::System& system_) 105KConditionVariable::KConditionVariable(Core::System& system_)
@@ -78,84 +121,77 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
78 121
79 // Determine the next tag. 122 // Determine the next tag.
80 u32 next_value{}; 123 u32 next_value{};
81 if (next_owner_thread) { 124 if (next_owner_thread != nullptr) {
82 next_value = next_owner_thread->GetAddressKeyValue(); 125 next_value = next_owner_thread->GetAddressKeyValue();
83 if (num_waiters > 1) { 126 if (num_waiters > 1) {
84 next_value |= Svc::HandleWaitMask; 127 next_value |= Svc::HandleWaitMask;
85 } 128 }
86 129
87 next_owner_thread->SetSyncedObject(nullptr, ResultSuccess); 130 // Write the value to userspace.
88 next_owner_thread->Wakeup(); 131 ResultCode result{ResultSuccess};
89 } 132 if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
90 133 result = ResultSuccess;
91 // Write the value to userspace. 134 } else {
92 if (!WriteToUser(system, addr, std::addressof(next_value))) { 135 result = ResultInvalidCurrentMemory;
93 if (next_owner_thread) {
94 next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
95 } 136 }
96 137
97 return ResultInvalidCurrentMemory; 138 // Signal the next owner thread.
139 next_owner_thread->EndWait(result);
140 return result;
141 } else {
142 // Just write the value to userspace.
143 R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
144 ResultInvalidCurrentMemory);
145
146 return ResultSuccess;
98 } 147 }
99 } 148 }
100
101 return ResultSuccess;
102} 149}
103 150
104ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { 151ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
105 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); 152 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
153 ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
106 154
107 // Wait for the address. 155 // Wait for the address.
156 KThread* owner_thread{};
108 { 157 {
109 KScopedAutoObject<KThread> owner_thread; 158 KScopedSchedulerLock sl(kernel);
110 ASSERT(owner_thread.IsNull());
111 {
112 KScopedSchedulerLock sl(kernel);
113 cur_thread->SetSyncedObject(nullptr, ResultSuccess);
114 159
115 // Check if the thread should terminate. 160 // Check if the thread should terminate.
116 R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested); 161 R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
117 162
118 { 163 // Read the tag from userspace.
119 // Read the tag from userspace. 164 u32 test_tag{};
120 u32 test_tag{}; 165 R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
121 R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
122 ResultInvalidCurrentMemory);
123
124 // If the tag isn't the handle (with wait mask), we're done.
125 R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
126
127 // Get the lock owner thread.
128 owner_thread =
129 kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
130 handle);
131 R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
132
133 // Update the lock.
134 cur_thread->SetAddressKey(addr, value);
135 owner_thread->AddWaiter(cur_thread);
136 cur_thread->SetState(ThreadState::Waiting);
137 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
138 cur_thread->SetMutexWaitAddressForDebugging(addr);
139 }
140 }
141 ASSERT(owner_thread.IsNotNull());
142 }
143 166
144 // Remove the thread as a waiter from the lock owner. 167 // If the tag isn't the handle (with wait mask), we're done.
145 { 168 R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
146 KScopedSchedulerLock sl(kernel); 169
147 KThread* owner_thread = cur_thread->GetLockOwner(); 170 // Get the lock owner thread.
148 if (owner_thread != nullptr) { 171 owner_thread = kernel.CurrentProcess()
149 owner_thread->RemoveWaiter(cur_thread); 172 ->GetHandleTable()
150 } 173 .GetObjectWithoutPseudoHandle<KThread>(handle)
174 .ReleasePointerUnsafe();
175 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
176
177 // Update the lock.
178 cur_thread->SetAddressKey(addr, value);
179 owner_thread->AddWaiter(cur_thread);
180
181 // Begin waiting.
182 cur_thread->BeginWait(std::addressof(wait_queue));
183 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
184 cur_thread->SetMutexWaitAddressForDebugging(addr);
151 } 185 }
152 186
187 // Close our reference to the owner thread, now that the wait is over.
188 owner_thread->Close();
189
153 // Get the wait result. 190 // Get the wait result.
154 KSynchronizationObject* dummy{}; 191 return cur_thread->GetWaitResult();
155 return cur_thread->GetWaitResult(std::addressof(dummy));
156} 192}
157 193
158KThread* KConditionVariable::SignalImpl(KThread* thread) { 194void KConditionVariable::SignalImpl(KThread* thread) {
159 // Check pre-conditions. 195 // Check pre-conditions.
160 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 196 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
161 197
@@ -169,18 +205,16 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
169 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 205 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
170 // TODO(bunnei): We should call CanAccessAtomic(..) here. 206 // TODO(bunnei): We should call CanAccessAtomic(..) here.
171 can_access = true; 207 can_access = true;
172 if (can_access) { 208 if (can_access) [[likely]] {
173 UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag, 209 UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
174 Svc::HandleWaitMask); 210 Svc::HandleWaitMask);
175 } 211 }
176 } 212 }
177 213
178 KThread* thread_to_close = nullptr; 214 if (can_access) [[likely]] {
179 if (can_access) {
180 if (prev_tag == Svc::InvalidHandle) { 215 if (prev_tag == Svc::InvalidHandle) {
181 // If nobody held the lock previously, we're all good. 216 // If nobody held the lock previously, we're all good.
182 thread->SetSyncedObject(nullptr, ResultSuccess); 217 thread->EndWait(ResultSuccess);
183 thread->Wakeup();
184 } else { 218 } else {
185 // Get the previous owner. 219 // Get the previous owner.
186 KThread* owner_thread = kernel.CurrentProcess() 220 KThread* owner_thread = kernel.CurrentProcess()
@@ -189,33 +223,22 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
189 static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask)) 223 static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
190 .ReleasePointerUnsafe(); 224 .ReleasePointerUnsafe();
191 225
192 if (owner_thread) { 226 if (owner_thread) [[likely]] {
193 // Add the thread as a waiter on the owner. 227 // Add the thread as a waiter on the owner.
194 owner_thread->AddWaiter(thread); 228 owner_thread->AddWaiter(thread);
195 thread_to_close = owner_thread; 229 owner_thread->Close();
196 } else { 230 } else {
197 // The lock was tagged with a thread that doesn't exist. 231 // The lock was tagged with a thread that doesn't exist.
198 thread->SetSyncedObject(nullptr, ResultInvalidState); 232 thread->EndWait(ResultInvalidState);
199 thread->Wakeup();
200 } 233 }
201 } 234 }
202 } else { 235 } else {
203 // If the address wasn't accessible, note so. 236 // If the address wasn't accessible, note so.
204 thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory); 237 thread->EndWait(ResultInvalidCurrentMemory);
205 thread->Wakeup();
206 } 238 }
207
208 return thread_to_close;
209} 239}
210 240
211void KConditionVariable::Signal(u64 cv_key, s32 count) { 241void KConditionVariable::Signal(u64 cv_key, s32 count) {
212 // Prepare for signaling.
213 constexpr int MaxThreads = 16;
214
215 KLinkedList<KThread> thread_list{kernel};
216 std::array<KThread*, MaxThreads> thread_array;
217 s32 num_to_close{};
218
219 // Perform signaling. 242 // Perform signaling.
220 s32 num_waiters{}; 243 s32 num_waiters{};
221 { 244 {
@@ -226,14 +249,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
226 (it->GetConditionVariableKey() == cv_key)) { 249 (it->GetConditionVariableKey() == cv_key)) {
227 KThread* target_thread = std::addressof(*it); 250 KThread* target_thread = std::addressof(*it);
228 251
229 if (KThread* thread = SignalImpl(target_thread); thread != nullptr) { 252 this->SignalImpl(target_thread);
230 if (num_to_close < MaxThreads) {
231 thread_array[num_to_close++] = thread;
232 } else {
233 thread_list.push_back(*thread);
234 }
235 }
236
237 it = thread_tree.erase(it); 253 it = thread_tree.erase(it);
238 target_thread->ClearConditionVariable(); 254 target_thread->ClearConditionVariable();
239 ++num_waiters; 255 ++num_waiters;
@@ -245,27 +261,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
245 WriteToUser(system, cv_key, std::addressof(has_waiter_flag)); 261 WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
246 } 262 }
247 } 263 }
248
249 // Close threads in the array.
250 for (auto i = 0; i < num_to_close; ++i) {
251 thread_array[i]->Close();
252 }
253
254 // Close threads in the list.
255 for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
256 (*it).Close();
257 }
258} 264}
259 265
260ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { 266ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
261 // Prepare to wait. 267 // Prepare to wait.
262 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); 268 KThread* cur_thread = GetCurrentThreadPointer(kernel);
269 ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
270 kernel, std::addressof(thread_tree));
263 271
264 { 272 {
265 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; 273 KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
266
267 // Set the synced object.
268 cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
269 274
270 // Check that the thread isn't terminating. 275 // Check that the thread isn't terminating.
271 if (cur_thread->IsTerminationRequested()) { 276 if (cur_thread->IsTerminationRequested()) {
@@ -290,8 +295,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
290 } 295 }
291 296
292 // Wake up the next owner. 297 // Wake up the next owner.
293 next_owner_thread->SetSyncedObject(nullptr, ResultSuccess); 298 next_owner_thread->EndWait(ResultSuccess);
294 next_owner_thread->Wakeup();
295 } 299 }
296 300
297 // Write to the cv key. 301 // Write to the cv key.
@@ -308,40 +312,21 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
308 } 312 }
309 } 313 }
310 314
311 // Update condition variable tracking. 315 // If timeout is zero, time out.
312 { 316 R_UNLESS(timeout != 0, ResultTimedOut);
313 cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
314 thread_tree.insert(*cur_thread);
315 }
316 317
317 // If the timeout is non-zero, set the thread as waiting. 318 // Update condition variable tracking.
318 if (timeout != 0) { 319 cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
319 cur_thread->SetState(ThreadState::Waiting); 320 thread_tree.insert(*cur_thread);
320 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
321 cur_thread->SetMutexWaitAddressForDebugging(addr);
322 }
323 }
324
325 // Cancel the timer wait.
326 kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
327
328 // Remove from the condition variable.
329 {
330 KScopedSchedulerLock sl(kernel);
331
332 if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
333 owner->RemoveWaiter(cur_thread);
334 }
335 321
336 if (cur_thread->IsWaitingForConditionVariable()) { 322 // Begin waiting.
337 thread_tree.erase(thread_tree.iterator_to(*cur_thread)); 323 cur_thread->BeginWait(std::addressof(wait_queue));
338 cur_thread->ClearConditionVariable(); 324 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
339 } 325 cur_thread->SetMutexWaitAddressForDebugging(addr);
340 } 326 }
341 327
342 // Get the result. 328 // Get the wait result.
343 KSynchronizationObject* dummy{}; 329 return cur_thread->GetWaitResult();
344 return cur_thread->GetWaitResult(std::addressof(dummy));
345} 330}
346 331
347} // namespace Kernel 332} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h
index 861dbd420..5e4815d08 100644
--- a/src/core/hle/kernel/k_condition_variable.h
+++ b/src/core/hle/kernel/k_condition_variable.h
@@ -34,7 +34,7 @@ public:
34 [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); 34 [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
35 35
36private: 36private:
37 [[nodiscard]] KThread* SignalImpl(KThread* thread); 37 void SignalImpl(KThread* thread);
38 38
39 ThreadTree thread_tree; 39 ThreadTree thread_tree;
40 40
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index e90fc0628..cf95f0852 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
13 // Get the table and clear our record of it. 13 // Get the table and clear our record of it.
14 u16 saved_table_size = 0; 14 u16 saved_table_size = 0;
15 { 15 {
16 KScopedDisableDispatch dd(kernel);
16 KScopedSpinLock lk(m_lock); 17 KScopedSpinLock lk(m_lock);
17 18
18 std::swap(m_table_size, saved_table_size); 19 std::swap(m_table_size, saved_table_size);
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
43 // Find the object and free the entry. 44 // Find the object and free the entry.
44 KAutoObject* obj = nullptr; 45 KAutoObject* obj = nullptr;
45 { 46 {
47 KScopedDisableDispatch dd(kernel);
46 KScopedSpinLock lk(m_lock); 48 KScopedSpinLock lk(m_lock);
47 49
48 if (this->IsValidHandle(handle)) { 50 if (this->IsValidHandle(handle)) {
@@ -62,6 +64,7 @@ bool KHandleTable::Remove(Handle handle) {
62} 64}
63 65
64ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { 66ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
67 KScopedDisableDispatch dd(kernel);
65 KScopedSpinLock lk(m_lock); 68 KScopedSpinLock lk(m_lock);
66 69
67 // Never exceed our capacity. 70 // Never exceed our capacity.
@@ -84,6 +87,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
84} 87}
85 88
86ResultCode KHandleTable::Reserve(Handle* out_handle) { 89ResultCode KHandleTable::Reserve(Handle* out_handle) {
90 KScopedDisableDispatch dd(kernel);
87 KScopedSpinLock lk(m_lock); 91 KScopedSpinLock lk(m_lock);
88 92
89 // Never exceed our capacity. 93 // Never exceed our capacity.
@@ -94,6 +98,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
94} 98}
95 99
96void KHandleTable::Unreserve(Handle handle) { 100void KHandleTable::Unreserve(Handle handle) {
101 KScopedDisableDispatch dd(kernel);
97 KScopedSpinLock lk(m_lock); 102 KScopedSpinLock lk(m_lock);
98 103
99 // Unpack the handle. 104 // Unpack the handle.
@@ -112,6 +117,7 @@ void KHandleTable::Unreserve(Handle handle) {
112} 117}
113 118
114void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { 119void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
120 KScopedDisableDispatch dd(kernel);
115 KScopedSpinLock lk(m_lock); 121 KScopedSpinLock lk(m_lock);
116 122
117 // Unpack the handle. 123 // Unpack the handle.
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 95ec905ae..4b114ec2f 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -68,6 +68,7 @@ public:
68 template <typename T = KAutoObject> 68 template <typename T = KAutoObject>
69 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { 69 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
70 // Lock and look up in table. 70 // Lock and look up in table.
71 KScopedDisableDispatch dd(kernel);
71 KScopedSpinLock lk(m_lock); 72 KScopedSpinLock lk(m_lock);
72 73
73 if constexpr (std::is_same_v<T, KAutoObject>) { 74 if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -122,6 +123,7 @@ public:
122 size_t num_opened; 123 size_t num_opened;
123 { 124 {
124 // Lock the table. 125 // Lock the table.
126 KScopedDisableDispatch dd(kernel);
125 KScopedSpinLock lk(m_lock); 127 KScopedSpinLock lk(m_lock);
126 for (num_opened = 0; num_opened < num_handles; num_opened++) { 128 for (num_opened = 0; num_opened < num_handles; num_opened++) {
127 // Get the current handle. 129 // Get the current handle.
diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp
new file mode 100644
index 000000000..a8001fffc
--- /dev/null
+++ b/src/core/hle/kernel/k_light_condition_variable.cpp
@@ -0,0 +1,80 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/k_light_condition_variable.h"
6#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
8#include "core/hle/kernel/k_thread_queue.h"
9#include "core/hle/kernel/svc_results.h"
10
11namespace Kernel {
12
13namespace {
14
15class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
16public:
17 ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl,
18 bool term)
19 : KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
20
21 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
22 bool cancel_timer_task) override {
23 // Only process waits if we're allowed to.
24 if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
25 return;
26 }
27
28 // Remove the thread from the waiting thread from the light condition variable.
29 m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
30
31 // Invoke the base cancel wait handler.
32 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
33 }
34
35private:
36 KThread::WaiterList* m_wait_list;
37 bool m_allow_terminating_thread;
38};
39
40} // namespace
41
42void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
43 // Create thread queue.
44 KThread* owner = GetCurrentThreadPointer(kernel);
45
46 ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
47 allow_terminating_thread);
48
49 // Sleep the thread.
50 {
51 KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
52
53 if (!allow_terminating_thread && owner->IsTerminationRequested()) {
54 lk.CancelSleep();
55 return;
56 }
57
58 lock->Unlock();
59
60 // Add the thread to the queue.
61 wait_list.push_back(*owner);
62
63 // Begin waiting.
64 owner->BeginWait(std::addressof(wait_queue));
65 }
66
67 // Re-acquire the lock.
68 lock->Lock();
69}
70
71void KLightConditionVariable::Broadcast() {
72 KScopedSchedulerLock lk(kernel);
73
74 // Signal all threads.
75 for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
76 it->EndWait(ResultSuccess);
77 }
78}
79
80} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h
index fb0ad783a..5d6d7f128 100644
--- a/src/core/hle/kernel/k_light_condition_variable.h
+++ b/src/core/hle/kernel/k_light_condition_variable.h
@@ -2,72 +2,24 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5// This file references various implementation details from Atmosphere, an open-source firmware for
6// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
7
8#pragma once 5#pragma once
9 6
10#include "common/common_types.h" 7#include "common/common_types.h"
11#include "core/hle/kernel/k_scheduler.h" 8#include "core/hle/kernel/k_thread.h"
12#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
13#include "core/hle/kernel/time_manager.h"
14 9
15namespace Kernel { 10namespace Kernel {
11
16class KernelCore; 12class KernelCore;
13class KLightLock;
17 14
18class KLightConditionVariable { 15class KLightConditionVariable {
19public: 16public:
20 explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {} 17 explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
21 18
22 void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) { 19 void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
23 WaitImpl(lock, timeout, allow_terminating_thread); 20 void Broadcast();
24 }
25
26 void Broadcast() {
27 KScopedSchedulerLock lk{kernel};
28
29 // Signal all threads.
30 for (auto& thread : wait_list) {
31 thread.SetState(ThreadState::Runnable);
32 }
33 }
34 21
35private: 22private:
36 void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
37 KThread* owner = GetCurrentThreadPointer(kernel);
38
39 // Sleep the thread.
40 {
41 KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
42
43 if (!allow_terminating_thread && owner->IsTerminationRequested()) {
44 lk.CancelSleep();
45 return;
46 }
47
48 lock->Unlock();
49
50 // Set the thread as waiting.
51 GetCurrentThread(kernel).SetState(ThreadState::Waiting);
52
53 // Add the thread to the queue.
54 wait_list.push_back(GetCurrentThread(kernel));
55 }
56
57 // Remove the thread from the wait list.
58 {
59 KScopedSchedulerLock sl{kernel};
60
61 wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
62 }
63
64 // Cancel the task that the sleep setup.
65 kernel.TimeManager().UnscheduleTimeEvent(owner);
66
67 // Re-acquire the lock.
68 lock->Lock();
69 }
70
71 KernelCore& kernel; 23 KernelCore& kernel;
72 KThread::WaiterList wait_list{}; 24 KThread::WaiterList wait_list{};
73}; 25};
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 0896e705f..4620342eb 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -5,44 +5,59 @@
5#include "core/hle/kernel/k_light_lock.h" 5#include "core/hle/kernel/k_light_lock.h"
6#include "core/hle/kernel/k_scheduler.h" 6#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_thread.h" 7#include "core/hle/kernel/k_thread.h"
8#include "core/hle/kernel/k_thread_queue.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
9 10
10namespace Kernel { 11namespace Kernel {
11 12
13namespace {
14
15class ThreadQueueImplForKLightLock final : public KThreadQueue {
16public:
17 explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
18
19 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
20 bool cancel_timer_task) override {
21 // Remove the thread as a waiter from its owner.
22 if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
23 owner->RemoveWaiter(waiting_thread);
24 }
25
26 // Invoke the base cancel wait handler.
27 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
28 }
29};
30
31} // namespace
32
12void KLightLock::Lock() { 33void KLightLock::Lock() {
13 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); 34 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
14 const uintptr_t cur_thread_tag = (cur_thread | 1);
15 35
16 while (true) { 36 while (true) {
17 uintptr_t old_tag = tag.load(std::memory_order_relaxed); 37 uintptr_t old_tag = tag.load(std::memory_order_relaxed);
18 38
19 while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1, 39 while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
20 std::memory_order_acquire)) { 40 std::memory_order_acquire)) {
21 if ((old_tag | 1) == cur_thread_tag) {
22 return;
23 }
24 } 41 }
25 42
26 if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) { 43 if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
27 break; 44 break;
28 } 45 }
29
30 LockSlowPath(old_tag | 1, cur_thread);
31 } 46 }
32} 47}
33 48
34void KLightLock::Unlock() { 49void KLightLock::Unlock() {
35 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); 50 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
51
36 uintptr_t expected = cur_thread; 52 uintptr_t expected = cur_thread;
37 do { 53 if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
38 if (expected != cur_thread) { 54 this->UnlockSlowPath(cur_thread);
39 return UnlockSlowPath(cur_thread); 55 }
40 }
41 } while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
42} 56}
43 57
44void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { 58bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
45 KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread); 59 KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
60 ThreadQueueImplForKLightLock wait_queue(kernel);
46 61
47 // Pend the current thread waiting on the owner thread. 62 // Pend the current thread waiting on the owner thread.
48 { 63 {
@@ -50,7 +65,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
50 65
51 // Ensure we actually have locking to do. 66 // Ensure we actually have locking to do.
52 if (tag.load(std::memory_order_relaxed) != _owner) { 67 if (tag.load(std::memory_order_relaxed) != _owner) {
53 return; 68 return false;
54 } 69 }
55 70
56 // Add the current thread as a waiter on the owner. 71 // Add the current thread as a waiter on the owner.
@@ -58,22 +73,15 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
58 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); 73 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
59 owner_thread->AddWaiter(cur_thread); 74 owner_thread->AddWaiter(cur_thread);
60 75
61 // Set thread states. 76 // Begin waiting to hold the lock.
62 cur_thread->SetState(ThreadState::Waiting); 77 cur_thread->BeginWait(std::addressof(wait_queue));
63 78
64 if (owner_thread->IsSuspended()) { 79 if (owner_thread->IsSuspended()) {
65 owner_thread->ContinueIfHasKernelWaiters(); 80 owner_thread->ContinueIfHasKernelWaiters();
66 } 81 }
67 } 82 }
68 83
69 // We're no longer waiting on the lock owner. 84 return true;
70 {
71 KScopedSchedulerLock sl{kernel};
72
73 if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
74 owner_thread->RemoveWaiter(cur_thread);
75 }
76 }
77} 85}
78 86
79void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { 87void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
@@ -81,22 +89,20 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
81 89
82 // Unlock. 90 // Unlock.
83 { 91 {
84 KScopedSchedulerLock sl{kernel}; 92 KScopedSchedulerLock sl(kernel);
85 93
86 // Get the next owner. 94 // Get the next owner.
87 s32 num_waiters = 0; 95 s32 num_waiters;
88 KThread* next_owner = owner_thread->RemoveWaiterByKey( 96 KThread* next_owner = owner_thread->RemoveWaiterByKey(
89 std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag))); 97 std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
90 98
91 // Pass the lock to the next owner. 99 // Pass the lock to the next owner.
92 uintptr_t next_tag = 0; 100 uintptr_t next_tag = 0;
93 if (next_owner != nullptr) { 101 if (next_owner != nullptr) {
94 next_tag = reinterpret_cast<uintptr_t>(next_owner); 102 next_tag =
95 if (num_waiters > 1) { 103 reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
96 next_tag |= 0x1;
97 }
98 104
99 next_owner->SetState(ThreadState::Runnable); 105 next_owner->EndWait(ResultSuccess);
100 106
101 if (next_owner->IsSuspended()) { 107 if (next_owner->IsSuspended()) {
102 next_owner->ContinueIfHasKernelWaiters(); 108 next_owner->ContinueIfHasKernelWaiters();
@@ -110,7 +116,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
110 } 116 }
111 117
112 // Write the new tag value. 118 // Write the new tag value.
113 tag.store(next_tag); 119 tag.store(next_tag, std::memory_order_release);
114 } 120 }
115} 121}
116 122
diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h
index ad853661d..4163b8a85 100644
--- a/src/core/hle/kernel/k_light_lock.h
+++ b/src/core/hle/kernel/k_light_lock.h
@@ -20,7 +20,7 @@ public:
20 20
21 void Unlock(); 21 void Unlock();
22 22
23 void LockSlowPath(uintptr_t owner, uintptr_t cur_thread); 23 bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
24 24
25 void UnlockSlowPath(uintptr_t cur_thread); 25 void UnlockSlowPath(uintptr_t cur_thread);
26 26
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index a7fdb5fb8..fd491146f 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -131,6 +131,26 @@ enum class KMemoryPermission : u8 {
131 131
132 UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | 132 UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
133 Svc::MemoryPermission::Execute), 133 Svc::MemoryPermission::Execute),
134
135 KernelShift = 3,
136
137 KernelRead = Read << KernelShift,
138 KernelWrite = Write << KernelShift,
139 KernelExecute = Execute << KernelShift,
140
141 NotMapped = (1 << (2 * KernelShift)),
142
143 KernelReadWrite = KernelRead | KernelWrite,
144 KernelReadExecute = KernelRead | KernelExecute,
145
146 UserRead = Read | KernelRead,
147 UserWrite = Write | KernelWrite,
148 UserExecute = Execute,
149
150 UserReadWrite = UserRead | UserWrite,
151 UserReadExecute = UserRead | UserExecute,
152
153 IpcLockChangeMask = NotMapped | UserReadWrite
134}; 154};
135DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); 155DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
136 156
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 3362fb236..0e2ae582a 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -27,6 +27,10 @@ public:
27 return num_pages; 27 return num_pages;
28 } 28 }
29 29
30 constexpr std::size_t GetSize() const {
31 return GetNumPages() * PageSize;
32 }
33
30 private: 34 private:
31 u64 addr{}; 35 u64 addr{};
32 std::size_t num_pages{}; 36 std::size_t num_pages{};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 526b87241..99982e5a3 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -368,6 +368,33 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
368 return ResultSuccess; 368 return ResultSuccess;
369} 369}
370 370
371ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
372 KPageTable& src_page_table, VAddr src_addr) {
373 std::lock_guard lock{page_table_lock};
374
375 const std::size_t num_pages{size / PageSize};
376
377 // Check that the memory is mapped in the destination process.
378 size_t num_allocator_blocks;
379 R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
380 KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
381 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
382 KMemoryAttribute::None));
383
384 // Check that the memory is mapped in the source process.
385 R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
386 KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
387 KMemoryPermission::None, KMemoryAttribute::All,
388 KMemoryAttribute::None));
389
390 CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
391
392 // Apply the memory block update.
393 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
394 KMemoryAttribute::None);
395
396 return ResultSuccess;
397}
371void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) { 398void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
372 auto node{page_linked_list.Nodes().begin()}; 399 auto node{page_linked_list.Nodes().begin()};
373 PAddr map_addr{node->GetAddress()}; 400 PAddr map_addr{node->GetAddress()};
@@ -685,8 +712,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
685 return ResultSuccess; 712 return ResultSuccess;
686} 713}
687 714
688ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size, 715ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
689 KMemoryPermission perm) { 716 KMemoryPermission perm) {
690 717
691 std::lock_guard lock{page_table_lock}; 718 std::lock_guard lock{page_table_lock};
692 719
@@ -942,6 +969,60 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
942 return ResultSuccess; 969 return ResultSuccess;
943} 970}
944 971
972ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
973 std::lock_guard lock{page_table_lock};
974
975 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
976
977 KMemoryPermission old_perm{};
978
979 if (const ResultCode result{CheckMemoryState(
980 nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
981 KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
982 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
983 result.IsError()) {
984 return result;
985 }
986
987 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
988
989 block_manager->UpdateLock(
990 addr, size / PageSize,
991 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
992 block->ShareToDevice(permission);
993 },
994 new_perm);
995
996 return ResultSuccess;
997}
998
999ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1000 std::lock_guard lock{page_table_lock};
1001
1002 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
1003
1004 KMemoryPermission old_perm{};
1005
1006 if (const ResultCode result{CheckMemoryState(
1007 nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1008 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
1009 KMemoryAttribute::All, KMemoryAttribute::Locked)};
1010 result.IsError()) {
1011 return result;
1012 }
1013
1014 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1015
1016 block_manager->UpdateLock(
1017 addr, size / PageSize,
1018 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1019 block->UnshareToDevice(permission);
1020 },
1021 new_perm);
1022
1023 return ResultSuccess;
1024}
1025
945ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1026ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
946 block_manager = std::make_unique<KMemoryBlockManager>(start, end); 1027 block_manager = std::make_unique<KMemoryBlockManager>(start, end);
947 1028
@@ -1231,4 +1312,42 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
1231 return ResultSuccess; 1312 return ResultSuccess;
1232} 1313}
1233 1314
1315ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
1316 KMemoryState state_mask, KMemoryState state,
1317 KMemoryPermission perm_mask, KMemoryPermission perm,
1318 KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
1319 // Get information about the first block.
1320 const VAddr last_addr = addr + size - 1;
1321 KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
1322 KMemoryInfo info = it->GetMemoryInfo();
1323
1324 // If the start address isn't aligned, we need a block.
1325 const size_t blocks_for_start_align =
1326 (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
1327
1328 while (true) {
1329 // Validate against the provided masks.
1330 R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
1331
1332 // Break once we're done.
1333 if (last_addr <= info.GetLastAddress()) {
1334 break;
1335 }
1336
1337 // Advance our iterator.
1338 it++;
1339 info = it->GetMemoryInfo();
1340 }
1341
1342 // If the end address isn't aligned, we need a block.
1343 const size_t blocks_for_end_align =
1344 (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
1345
1346 if (out_blocks_needed != nullptr) {
1347 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
1348 }
1349
1350 return ResultSuccess;
1351}
1352
1234} // namespace Kernel 1353} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 770c4841c..d784aa67e 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -33,6 +33,8 @@ public:
33 KMemoryPermission perm); 33 KMemoryPermission perm);
34 ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 34 ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
35 ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 35 ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
36 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
37 VAddr src_addr);
36 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); 38 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
37 ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); 39 ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
38 ResultCode UnmapMemory(VAddr addr, std::size_t size); 40 ResultCode UnmapMemory(VAddr addr, std::size_t size);
@@ -41,7 +43,7 @@ public:
41 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, 43 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
42 KMemoryPermission perm); 44 KMemoryPermission perm);
43 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); 45 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
44 ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm); 46 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
45 KMemoryInfo QueryInfo(VAddr addr); 47 KMemoryInfo QueryInfo(VAddr addr);
46 ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); 48 ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
47 ResultCode ResetTransferMemory(VAddr addr, std::size_t size); 49 ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
@@ -55,6 +57,8 @@ public:
55 KMemoryPermission perm, PAddr map_addr = 0); 57 KMemoryPermission perm, PAddr map_addr = 0);
56 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); 58 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
57 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 59 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
60 ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
61 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
58 62
59 Common::PageTable& PageTableImpl() { 63 Common::PageTable& PageTableImpl() {
60 return page_table_impl; 64 return page_table_impl;
@@ -115,6 +119,10 @@ private:
115 return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, 119 return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
116 perm, attr_mask, attr, ignore_attr); 120 perm, attr_mask, attr, ignore_attr);
117 } 121 }
122 ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
123 KMemoryState state_mask, KMemoryState state,
124 KMemoryPermission perm_mask, KMemoryPermission perm,
125 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
118 126
119 std::recursive_mutex page_table_lock; 127 std::recursive_mutex page_table_lock;
120 std::unique_ptr<KMemoryBlockManager> block_manager; 128 std::unique_ptr<KMemoryBlockManager> block_manager;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 76fd8c285..90dda40dc 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -60,6 +60,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
60 thread->GetContext64().cpu_registers[0] = 0; 60 thread->GetContext64().cpu_registers[0] = 0;
61 thread->GetContext32().cpu_registers[1] = thread_handle; 61 thread->GetContext32().cpu_registers[1] = thread_handle;
62 thread->GetContext64().cpu_registers[1] = thread_handle; 62 thread->GetContext64().cpu_registers[1] = thread_handle;
63 thread->DisableDispatch();
63 64
64 auto& kernel = system.Kernel(); 65 auto& kernel = system.Kernel();
65 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 66 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
@@ -227,12 +228,15 @@ void KProcess::PinCurrentThread() {
227 const s32 core_id = GetCurrentCoreId(kernel); 228 const s32 core_id = GetCurrentCoreId(kernel);
228 KThread* cur_thread = GetCurrentThreadPointer(kernel); 229 KThread* cur_thread = GetCurrentThreadPointer(kernel);
229 230
230 // Pin it. 231 // If the thread isn't terminated, pin it.
231 PinThread(core_id, cur_thread); 232 if (!cur_thread->IsTerminationRequested()) {
232 cur_thread->Pin(); 233 // Pin it.
234 PinThread(core_id, cur_thread);
235 cur_thread->Pin();
233 236
234 // An update is needed. 237 // An update is needed.
235 KScheduler::SetSchedulerUpdateNeeded(kernel); 238 KScheduler::SetSchedulerUpdateNeeded(kernel);
239 }
236} 240}
237 241
238void KProcess::UnpinCurrentThread() { 242void KProcess::UnpinCurrentThread() {
@@ -250,6 +254,20 @@ void KProcess::UnpinCurrentThread() {
250 KScheduler::SetSchedulerUpdateNeeded(kernel); 254 KScheduler::SetSchedulerUpdateNeeded(kernel);
251} 255}
252 256
257void KProcess::UnpinThread(KThread* thread) {
258 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
259
260 // Get the thread's core id.
261 const auto core_id = thread->GetActiveCore();
262
263 // Unpin it.
264 UnpinThread(core_id, thread);
265 thread->Unpin();
266
267 // An update is needed.
268 KScheduler::SetSchedulerUpdateNeeded(kernel);
269}
270
253ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, 271ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
254 [[maybe_unused]] size_t size) { 272 [[maybe_unused]] size_t size) {
255 // Lock ourselves, to prevent concurrent access. 273 // Lock ourselves, to prevent concurrent access.
@@ -528,7 +546,7 @@ void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
528 std::lock_guard lock{HLE::g_hle_lock}; 546 std::lock_guard lock{HLE::g_hle_lock};
529 const auto ReprotectSegment = [&](const CodeSet::Segment& segment, 547 const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
530 KMemoryPermission permission) { 548 KMemoryPermission permission) {
531 page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); 549 page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
532 }; 550 };
533 551
534 kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), 552 kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 8a8c1fcbb..cb93c7e24 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -347,6 +347,7 @@ public:
347 347
348 void PinCurrentThread(); 348 void PinCurrentThread();
349 void UnpinCurrentThread(); 349 void UnpinCurrentThread();
350 void UnpinThread(KThread* thread);
350 351
351 KLightLock& GetStateLock() { 352 KLightLock& GetStateLock() {
352 return state_lock; 353 return state_lock;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 6a7d80d03..277201de4 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -240,8 +240,8 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
240 240
241 // If the thread is runnable, we want to change its priority in the queue. 241 // If the thread is runnable, we want to change its priority in the queue.
242 if (thread->GetRawState() == ThreadState::Runnable) { 242 if (thread->GetRawState() == ThreadState::Runnable) {
243 GetPriorityQueue(kernel).ChangePriority( 243 GetPriorityQueue(kernel).ChangePriority(old_priority,
244 old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread); 244 thread == kernel.GetCurrentEmuThread(), thread);
245 IncrementScheduledCount(thread); 245 IncrementScheduledCount(thread);
246 SetSchedulerUpdateNeeded(kernel); 246 SetSchedulerUpdateNeeded(kernel);
247 } 247 }
@@ -360,7 +360,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
360} 360}
361 361
362bool KScheduler::CanSchedule(KernelCore& kernel) { 362bool KScheduler::CanSchedule(KernelCore& kernel) {
363 return kernel.CurrentScheduler()->GetCurrentThread()->GetDisableDispatchCount() <= 1; 363 return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
364} 364}
365 365
366bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) { 366bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
@@ -376,20 +376,30 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
376} 376}
377 377
378void KScheduler::DisableScheduling(KernelCore& kernel) { 378void KScheduler::DisableScheduling(KernelCore& kernel) {
379 if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { 379 // If we are shutting down the kernel, none of this is relevant anymore.
380 ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0); 380 if (kernel.IsShuttingDown()) {
381 scheduler->GetCurrentThread()->DisableDispatch(); 381 return;
382 } 382 }
383
384 ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
385 GetCurrentThreadPointer(kernel)->DisableDispatch();
383} 386}
384 387
385void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { 388void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
386 if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { 389 // If we are shutting down the kernel, none of this is relevant anymore.
387 ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); 390 if (kernel.IsShuttingDown()) {
388 if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { 391 return;
389 scheduler->GetCurrentThread()->EnableDispatch(); 392 }
390 } 393
394 auto* current_thread = GetCurrentThreadPointer(kernel);
395
396 ASSERT(current_thread->GetDisableDispatchCount() >= 1);
397
398 if (current_thread->GetDisableDispatchCount() > 1) {
399 current_thread->EnableDispatch();
400 } else {
401 RescheduleCores(kernel, cores_needing_scheduling);
391 } 402 }
392 RescheduleCores(kernel, cores_needing_scheduling);
393} 403}
394 404
395u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { 405u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -617,13 +627,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
617 state.highest_priority_thread = nullptr; 627 state.highest_priority_thread = nullptr;
618} 628}
619 629
620KScheduler::~KScheduler() { 630void KScheduler::Finalize() {
621 if (idle_thread) { 631 if (idle_thread) {
622 idle_thread->Close(); 632 idle_thread->Close();
623 idle_thread = nullptr; 633 idle_thread = nullptr;
624 } 634 }
625} 635}
626 636
637KScheduler::~KScheduler() {
638 ASSERT(!idle_thread);
639}
640
627KThread* KScheduler::GetCurrentThread() const { 641KThread* KScheduler::GetCurrentThread() const {
628 if (auto result = current_thread.load(); result) { 642 if (auto result = current_thread.load(); result) {
629 return result; 643 return result;
@@ -642,10 +656,12 @@ void KScheduler::RescheduleCurrentCore() {
642 if (phys_core.IsInterrupted()) { 656 if (phys_core.IsInterrupted()) {
643 phys_core.ClearInterrupt(); 657 phys_core.ClearInterrupt();
644 } 658 }
659
645 guard.Lock(); 660 guard.Lock();
646 if (state.needs_scheduling.load()) { 661 if (state.needs_scheduling.load()) {
647 Schedule(); 662 Schedule();
648 } else { 663 } else {
664 GetCurrentThread()->EnableDispatch();
649 guard.Unlock(); 665 guard.Unlock();
650 } 666 }
651} 667}
@@ -655,26 +671,33 @@ void KScheduler::OnThreadStart() {
655} 671}
656 672
657void KScheduler::Unload(KThread* thread) { 673void KScheduler::Unload(KThread* thread) {
674 ASSERT(thread);
675
658 LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); 676 LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
659 677
660 if (thread) { 678 if (thread->IsCallingSvc()) {
661 if (thread->IsCallingSvc()) { 679 thread->ClearIsCallingSvc();
662 thread->ClearIsCallingSvc(); 680 }
663 } 681
664 if (!thread->IsTerminationRequested()) { 682 auto& physical_core = system.Kernel().PhysicalCore(core_id);
665 prev_thread = thread; 683 if (!physical_core.IsInitialized()) {
666 684 return;
667 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); 685 }
668 cpu_core.SaveContext(thread->GetContext32()); 686
669 cpu_core.SaveContext(thread->GetContext64()); 687 Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
670 // Save the TPIDR_EL0 system register in case it was modified. 688 cpu_core.SaveContext(thread->GetContext32());
671 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); 689 cpu_core.SaveContext(thread->GetContext64());
672 cpu_core.ClearExclusiveState(); 690 // Save the TPIDR_EL0 system register in case it was modified.
673 } else { 691 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
674 prev_thread = nullptr; 692 cpu_core.ClearExclusiveState();
675 } 693
676 thread->context_guard.Unlock(); 694 if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
695 prev_thread = thread;
696 } else {
697 prev_thread = nullptr;
677 } 698 }
699
700 thread->context_guard.Unlock();
678} 701}
679 702
680void KScheduler::Reload(KThread* thread) { 703void KScheduler::Reload(KThread* thread) {
@@ -683,11 +706,6 @@ void KScheduler::Reload(KThread* thread) {
683 if (thread) { 706 if (thread) {
684 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); 707 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
685 708
686 auto* const thread_owner_process = thread->GetOwnerProcess();
687 if (thread_owner_process != nullptr) {
688 system.Kernel().MakeCurrentProcess(thread_owner_process);
689 }
690
691 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); 709 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
692 cpu_core.LoadContext(thread->GetContext32()); 710 cpu_core.LoadContext(thread->GetContext32());
693 cpu_core.LoadContext(thread->GetContext64()); 711 cpu_core.LoadContext(thread->GetContext64());
@@ -705,7 +723,7 @@ void KScheduler::SwitchContextStep2() {
705} 723}
706 724
707void KScheduler::ScheduleImpl() { 725void KScheduler::ScheduleImpl() {
708 KThread* previous_thread = current_thread.load(); 726 KThread* previous_thread = GetCurrentThread();
709 KThread* next_thread = state.highest_priority_thread; 727 KThread* next_thread = state.highest_priority_thread;
710 728
711 state.needs_scheduling = false; 729 state.needs_scheduling = false;
@@ -717,10 +735,15 @@ void KScheduler::ScheduleImpl() {
717 735
718 // If we're not actually switching thread, there's nothing to do. 736 // If we're not actually switching thread, there's nothing to do.
719 if (next_thread == current_thread.load()) { 737 if (next_thread == current_thread.load()) {
738 previous_thread->EnableDispatch();
720 guard.Unlock(); 739 guard.Unlock();
721 return; 740 return;
722 } 741 }
723 742
743 if (next_thread->GetCurrentCore() != core_id) {
744 next_thread->SetCurrentCore(core_id);
745 }
746
724 current_thread.store(next_thread); 747 current_thread.store(next_thread);
725 748
726 KProcess* const previous_process = system.Kernel().CurrentProcess(); 749 KProcess* const previous_process = system.Kernel().CurrentProcess();
@@ -731,11 +754,7 @@ void KScheduler::ScheduleImpl() {
731 Unload(previous_thread); 754 Unload(previous_thread);
732 755
733 std::shared_ptr<Common::Fiber>* old_context; 756 std::shared_ptr<Common::Fiber>* old_context;
734 if (previous_thread != nullptr) { 757 old_context = &previous_thread->GetHostContext();
735 old_context = &previous_thread->GetHostContext();
736 } else {
737 old_context = &idle_thread->GetHostContext();
738 }
739 guard.Unlock(); 758 guard.Unlock();
740 759
741 Common::Fiber::YieldTo(*old_context, *switch_fiber); 760 Common::Fiber::YieldTo(*old_context, *switch_fiber);
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 7df288438..82fcd99e7 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -33,6 +33,8 @@ public:
33 explicit KScheduler(Core::System& system_, s32 core_id_); 33 explicit KScheduler(Core::System& system_, s32 core_id_);
34 ~KScheduler(); 34 ~KScheduler();
35 35
36 void Finalize();
37
36 /// Reschedules to the next available thread (call after current thread is suspended) 38 /// Reschedules to the next available thread (call after current thread is suspended)
37 void RescheduleCurrentCore(); 39 void RescheduleCurrentCore();
38 40
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index c571f2992..93c47f1b1 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -23,6 +23,11 @@ public:
23 } 23 }
24 24
25 void Lock() { 25 void Lock() {
26 // If we are shutting down the kernel, none of this is relevant anymore.
27 if (kernel.IsShuttingDown()) {
28 return;
29 }
30
26 if (IsLockedByCurrentThread()) { 31 if (IsLockedByCurrentThread()) {
27 // If we already own the lock, we can just increment the count. 32 // If we already own the lock, we can just increment the count.
28 ASSERT(lock_count > 0); 33 ASSERT(lock_count > 0);
@@ -43,6 +48,11 @@ public:
43 } 48 }
44 49
45 void Unlock() { 50 void Unlock() {
51 // If we are shutting down the kernel, none of this is relevant anymore.
52 if (kernel.IsShuttingDown()) {
53 return;
54 }
55
46 ASSERT(IsLockedByCurrentThread()); 56 ASSERT(IsLockedByCurrentThread());
47 ASSERT(lock_count > 0); 57 ASSERT(lock_count > 0);
48 58
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index 61dc2858f..2995c492d 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -8,6 +8,7 @@
8#pragma once 8#pragma once
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/global_scheduler_context.h"
11#include "core/hle/kernel/k_thread.h" 12#include "core/hle/kernel/k_thread.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/time_manager.h" 14#include "core/hle/kernel/time_manager.h"
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 2bd53ccbd..d4e4a6b06 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -175,8 +175,7 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
175 { 175 {
176 KScopedSchedulerLock lock(kernel); 176 KScopedSchedulerLock lock(kernel);
177 if (!context.IsThreadWaiting()) { 177 if (!context.IsThreadWaiting()) {
178 context.GetThread().Wakeup(); 178 context.GetThread().EndWait(result);
179 context.GetThread().SetSyncedObject(nullptr, result);
180 } 179 }
181 } 180 }
182 181
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index f168b4f21..e4c5eb74f 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -8,11 +8,66 @@
8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
9#include "core/hle/kernel/k_synchronization_object.h" 9#include "core/hle/kernel/k_synchronization_object.h"
10#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
11#include "core/hle/kernel/k_thread_queue.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/svc_results.h" 13#include "core/hle/kernel/svc_results.h"
13 14
14namespace Kernel { 15namespace Kernel {
15 16
17namespace {
18
19class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
20public:
21 ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
22 KSynchronizationObject::ThreadListNode* n, s32 c)
23 : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
24
25 void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
26 ResultCode wait_result) override {
27 // Determine the sync index, and unlink all nodes.
28 s32 sync_index = -1;
29 for (auto i = 0; i < m_count; ++i) {
30 // Check if this is the signaled object.
31 if (m_objects[i] == signaled_object && sync_index == -1) {
32 sync_index = i;
33 }
34
35 // Unlink the current node from the current object.
36 m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
37 }
38
39 // Set the waiting thread's sync index.
40 waiting_thread->SetSyncedIndex(sync_index);
41
42 // Set the waiting thread as not cancellable.
43 waiting_thread->ClearCancellable();
44
45 // Invoke the base end wait handler.
46 KThreadQueue::EndWait(waiting_thread, wait_result);
47 }
48
49 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
50 bool cancel_timer_task) override {
51 // Remove all nodes from our list.
52 for (auto i = 0; i < m_count; ++i) {
53 m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
54 }
55
56 // Set the waiting thread as not cancellable.
57 waiting_thread->ClearCancellable();
58
59 // Invoke the base cancel wait handler.
60 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
61 }
62
63private:
64 KSynchronizationObject** m_objects;
65 KSynchronizationObject::ThreadListNode* m_nodes;
66 s32 m_count;
67};
68
69} // namespace
70
16void KSynchronizationObject::Finalize() { 71void KSynchronizationObject::Finalize() {
17 this->OnFinalizeSynchronizationObject(); 72 this->OnFinalizeSynchronizationObject();
18 KAutoObject::Finalize(); 73 KAutoObject::Finalize();
@@ -25,11 +80,19 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
25 std::vector<ThreadListNode> thread_nodes(num_objects); 80 std::vector<ThreadListNode> thread_nodes(num_objects);
26 81
27 // Prepare for wait. 82 // Prepare for wait.
28 KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread(); 83 KThread* thread = GetCurrentThreadPointer(kernel_ctx);
84 ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
85 thread_nodes.data(), num_objects);
29 86
30 { 87 {
31 // Setup the scheduling lock and sleep. 88 // Setup the scheduling lock and sleep.
32 KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout}; 89 KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
90
91 // Check if the thread should terminate.
92 if (thread->IsTerminationRequested()) {
93 slp.CancelSleep();
94 return ResultTerminationRequested;
95 }
33 96
34 // Check if any of the objects are already signaled. 97 // Check if any of the objects are already signaled.
35 for (auto i = 0; i < num_objects; ++i) { 98 for (auto i = 0; i < num_objects; ++i) {
@@ -48,12 +111,6 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
48 return ResultTimedOut; 111 return ResultTimedOut;
49 } 112 }
50 113
51 // Check if the thread should terminate.
52 if (thread->IsTerminationRequested()) {
53 slp.CancelSleep();
54 return ResultTerminationRequested;
55 }
56
57 // Check if waiting was canceled. 114 // Check if waiting was canceled.
58 if (thread->IsWaitCancelled()) { 115 if (thread->IsWaitCancelled()) {
59 slp.CancelSleep(); 116 slp.CancelSleep();
@@ -66,73 +123,25 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
66 thread_nodes[i].thread = thread; 123 thread_nodes[i].thread = thread;
67 thread_nodes[i].next = nullptr; 124 thread_nodes[i].next = nullptr;
68 125
69 if (objects[i]->thread_list_tail == nullptr) { 126 objects[i]->LinkNode(std::addressof(thread_nodes[i]));
70 objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
71 } else {
72 objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
73 }
74
75 objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
76 } 127 }
77 128
78 // For debugging only 129 // Mark the thread as cancellable.
79 thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
80
81 // Mark the thread as waiting.
82 thread->SetCancellable(); 130 thread->SetCancellable();
83 thread->SetSyncedObject(nullptr, ResultTimedOut);
84 thread->SetState(ThreadState::Waiting);
85 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
86 }
87 131
88 // The lock/sleep is done, so we should be able to get our result. 132 // Clear the thread's synced index.
133 thread->SetSyncedIndex(-1);
89 134
90 // Thread is no longer cancellable. 135 // Wait for an object to be signaled.
91 thread->ClearCancellable(); 136 thread->BeginWait(std::addressof(wait_queue));
92 137 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
93 // For debugging only 138 }
94 thread->SetWaitObjectsForDebugging({});
95 139
96 // Cancel the timer as needed. 140 // Set the output index.
97 kernel_ctx.TimeManager().UnscheduleTimeEvent(thread); 141 *out_index = thread->GetSyncedIndex();
98 142
99 // Get the wait result. 143 // Get the wait result.
100 ResultCode wait_result{ResultSuccess}; 144 return thread->GetWaitResult();
101 s32 sync_index = -1;
102 {
103 KScopedSchedulerLock lock(kernel_ctx);
104 KSynchronizationObject* synced_obj;
105 wait_result = thread->GetWaitResult(std::addressof(synced_obj));
106
107 for (auto i = 0; i < num_objects; ++i) {
108 // Unlink the object from the list.
109 ThreadListNode* prev_ptr =
110 reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
111 ThreadListNode* prev_val = nullptr;
112 ThreadListNode *prev, *tail_prev;
113
114 do {
115 prev = prev_ptr;
116 prev_ptr = prev_ptr->next;
117 tail_prev = prev_val;
118 prev_val = prev_ptr;
119 } while (prev_ptr != std::addressof(thread_nodes[i]));
120
121 if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
122 objects[i]->thread_list_tail = tail_prev;
123 }
124
125 prev->next = thread_nodes[i].next;
126
127 if (objects[i] == synced_obj) {
128 sync_index = i;
129 }
130 }
131 }
132
133 // Set output.
134 *out_index = sync_index;
135 return wait_result;
136} 145}
137 146
138KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_) 147KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
@@ -141,7 +150,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
141KSynchronizationObject::~KSynchronizationObject() = default; 150KSynchronizationObject::~KSynchronizationObject() = default;
142 151
143void KSynchronizationObject::NotifyAvailable(ResultCode result) { 152void KSynchronizationObject::NotifyAvailable(ResultCode result) {
144 KScopedSchedulerLock lock(kernel); 153 KScopedSchedulerLock sl(kernel);
145 154
146 // If we're not signaled, we've nothing to notify. 155 // If we're not signaled, we've nothing to notify.
147 if (!this->IsSignaled()) { 156 if (!this->IsSignaled()) {
@@ -150,11 +159,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
150 159
151 // Iterate over each thread. 160 // Iterate over each thread.
152 for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { 161 for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
153 KThread* thread = cur_node->thread; 162 cur_node->thread->NotifyAvailable(this, result);
154 if (thread->GetState() == ThreadState::Waiting) {
155 thread->SetSyncedObject(this, result);
156 thread->SetState(ThreadState::Runnable);
157 }
158 } 163 }
159} 164}
160 165
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
index 898e58e16..ec235437b 100644
--- a/src/core/hle/kernel/k_synchronization_object.h
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -35,6 +35,38 @@ public:
35 35
36 [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const; 36 [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
37 37
38 void LinkNode(ThreadListNode* node_) {
39 // Link the node to the list.
40 if (thread_list_tail == nullptr) {
41 thread_list_head = node_;
42 } else {
43 thread_list_tail->next = node_;
44 }
45
46 thread_list_tail = node_;
47 }
48
49 void UnlinkNode(ThreadListNode* node_) {
50 // Unlink the node from the list.
51 ThreadListNode* prev_ptr =
52 reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
53 ThreadListNode* prev_val = nullptr;
54 ThreadListNode *prev, *tail_prev;
55
56 do {
57 prev = prev_ptr;
58 prev_ptr = prev_ptr->next;
59 tail_prev = prev_val;
60 prev_val = prev_ptr;
61 } while (prev_ptr != node_);
62
63 if (thread_list_tail == node_) {
64 thread_list_tail = tail_prev;
65 }
66
67 prev->next = node_->next;
68 }
69
38protected: 70protected:
39 explicit KSynchronizationObject(KernelCore& kernel); 71 explicit KSynchronizationObject(KernelCore& kernel);
40 ~KSynchronizationObject() override; 72 ~KSynchronizationObject() override;
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index db65ce79a..752592e2e 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -13,6 +13,9 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/fiber.h" 14#include "common/fiber.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/scope_exit.h"
17#include "common/settings.h"
18#include "common/thread_queue_list.h"
16#include "core/core.h" 19#include "core/core.h"
17#include "core/cpu_manager.h" 20#include "core/cpu_manager.h"
18#include "core/hardware_properties.h" 21#include "core/hardware_properties.h"
@@ -56,6 +59,34 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
56 59
57namespace Kernel { 60namespace Kernel {
58 61
62namespace {
63
64class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
65public:
66 explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
67 : KThreadQueueWithoutEndWait(kernel_) {}
68};
69
70class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue {
71public:
72 explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl)
73 : KThreadQueue(kernel_), m_wait_list(wl) {}
74
75 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
76 bool cancel_timer_task) override {
77 // Remove the thread from the wait list.
78 m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
79
80 // Invoke the base cancel wait handler.
81 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
82 }
83
84private:
85 KThread::WaiterList* m_wait_list;
86};
87
88} // namespace
89
59KThread::KThread(KernelCore& kernel_) 90KThread::KThread(KernelCore& kernel_)
60 : KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {} 91 : KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {}
61KThread::~KThread() = default; 92KThread::~KThread() = default;
@@ -82,6 +113,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
82 [[fallthrough]]; 113 [[fallthrough]];
83 case ThreadType::HighPriority: 114 case ThreadType::HighPriority:
84 [[fallthrough]]; 115 [[fallthrough]];
116 case ThreadType::Dummy:
117 [[fallthrough]];
85 case ThreadType::User: 118 case ThreadType::User:
86 ASSERT(((owner == nullptr) || 119 ASSERT(((owner == nullptr) ||
87 (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); 120 (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
@@ -127,11 +160,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
127 priority = prio; 160 priority = prio;
128 base_priority = prio; 161 base_priority = prio;
129 162
130 // Set sync object and waiting lock to null.
131 synced_object = nullptr;
132
133 // Initialize sleeping queue. 163 // Initialize sleeping queue.
134 sleeping_queue = nullptr; 164 wait_queue = nullptr;
135 165
136 // Set suspend flags. 166 // Set suspend flags.
137 suspend_request_flags = 0; 167 suspend_request_flags = 0;
@@ -184,7 +214,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
184 // Setup the stack parameters. 214 // Setup the stack parameters.
185 StackParameters& sp = GetStackParameters(); 215 StackParameters& sp = GetStackParameters();
186 sp.cur_thread = this; 216 sp.cur_thread = this;
187 sp.disable_count = 1; 217 sp.disable_count = 0;
188 SetInExceptionHandler(); 218 SetInExceptionHandler();
189 219
190 // Set thread ID. 220 // Set thread ID.
@@ -211,15 +241,16 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
211 // Initialize the thread. 241 // Initialize the thread.
212 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); 242 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
213 243
214 // Initialize host context. 244 // Initialize emulation parameters.
215 thread->host_context = 245 thread->host_context =
216 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); 246 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
247 thread->is_single_core = !Settings::values.use_multi_core.GetValue();
217 248
218 return ResultSuccess; 249 return ResultSuccess;
219} 250}
220 251
221ResultCode KThread::InitializeDummyThread(KThread* thread) { 252ResultCode KThread::InitializeDummyThread(KThread* thread) {
222 return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Main); 253 return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy);
223} 254}
224 255
225ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { 256ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
@@ -273,11 +304,14 @@ void KThread::Finalize() {
273 304
274 auto it = waiter_list.begin(); 305 auto it = waiter_list.begin();
275 while (it != waiter_list.end()) { 306 while (it != waiter_list.end()) {
276 // The thread shouldn't be a kernel waiter. 307 // Clear the lock owner
277 it->SetLockOwner(nullptr); 308 it->SetLockOwner(nullptr);
278 it->SetSyncedObject(nullptr, ResultInvalidState); 309
279 it->Wakeup(); 310 // Erase the waiter from our list.
280 it = waiter_list.erase(it); 311 it = waiter_list.erase(it);
312
313 // Cancel the thread's wait.
314 it->CancelWait(ResultInvalidState, true);
281 } 315 }
282 } 316 }
283 317
@@ -294,15 +328,12 @@ bool KThread::IsSignaled() const {
294 return signaled; 328 return signaled;
295} 329}
296 330
297void KThread::Wakeup() { 331void KThread::OnTimer() {
298 KScopedSchedulerLock sl{kernel}; 332 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
299 333
334 // If we're waiting, cancel the wait.
300 if (GetState() == ThreadState::Waiting) { 335 if (GetState() == ThreadState::Waiting) {
301 if (sleeping_queue != nullptr) { 336 wait_queue->CancelWait(this, ResultTimedOut, false);
302 sleeping_queue->WakeupThread(this);
303 } else {
304 SetState(ThreadState::Runnable);
305 }
306 } 337 }
307} 338}
308 339
@@ -327,7 +358,7 @@ void KThread::StartTermination() {
327 358
328 // Signal. 359 // Signal.
329 signaled = true; 360 signaled = true;
330 NotifyAvailable(); 361 KSynchronizationObject::NotifyAvailable();
331 362
332 // Clear previous thread in KScheduler. 363 // Clear previous thread in KScheduler.
333 KScheduler::ClearPreviousThread(kernel, this); 364 KScheduler::ClearPreviousThread(kernel, this);
@@ -475,30 +506,32 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
475 return ResultSuccess; 506 return ResultSuccess;
476} 507}
477 508
478ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) { 509ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
479 ASSERT(parent != nullptr); 510 ASSERT(parent != nullptr);
480 ASSERT(v_affinity_mask != 0); 511 ASSERT(v_affinity_mask != 0);
481 KScopedLightLock lk{activity_pause_lock}; 512 KScopedLightLock lk(activity_pause_lock);
482 513
483 // Set the core mask. 514 // Set the core mask.
484 u64 p_affinity_mask = 0; 515 u64 p_affinity_mask = 0;
485 { 516 {
486 KScopedSchedulerLock sl{kernel}; 517 KScopedSchedulerLock sl(kernel);
487 ASSERT(num_core_migration_disables >= 0); 518 ASSERT(num_core_migration_disables >= 0);
488 519
489 // If the core id is no-update magic, preserve the ideal core id. 520 // If we're updating, set our ideal virtual core.
490 if (cpu_core_id == Svc::IdealCoreNoUpdate) { 521 if (core_id_ != Svc::IdealCoreNoUpdate) {
491 cpu_core_id = virtual_ideal_core_id; 522 virtual_ideal_core_id = core_id_;
492 R_UNLESS(((1ULL << cpu_core_id) & v_affinity_mask) != 0, ResultInvalidCombination); 523 } else {
524 // Preserve our ideal core id.
525 core_id_ = virtual_ideal_core_id;
526 R_UNLESS(((1ULL << core_id_) & v_affinity_mask) != 0, ResultInvalidCombination);
493 } 527 }
494 528
495 // Set the virtual core/affinity mask. 529 // Set our affinity mask.
496 virtual_ideal_core_id = cpu_core_id;
497 virtual_affinity_mask = v_affinity_mask; 530 virtual_affinity_mask = v_affinity_mask;
498 531
499 // Translate the virtual core to a physical core. 532 // Translate the virtual core to a physical core.
500 if (cpu_core_id >= 0) { 533 if (core_id_ >= 0) {
501 cpu_core_id = Core::Hardware::VirtualToPhysicalCoreMap[cpu_core_id]; 534 core_id_ = Core::Hardware::VirtualToPhysicalCoreMap[core_id_];
502 } 535 }
503 536
504 // Translate the virtual affinity mask to a physical one. 537 // Translate the virtual affinity mask to a physical one.
@@ -513,7 +546,7 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
513 const KAffinityMask old_mask = physical_affinity_mask; 546 const KAffinityMask old_mask = physical_affinity_mask;
514 547
515 // Set our new ideals. 548 // Set our new ideals.
516 physical_ideal_core_id = cpu_core_id; 549 physical_ideal_core_id = core_id_;
517 physical_affinity_mask.SetAffinityMask(p_affinity_mask); 550 physical_affinity_mask.SetAffinityMask(p_affinity_mask);
518 551
519 if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { 552 if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) {
@@ -531,18 +564,18 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
531 } 564 }
532 } else { 565 } else {
533 // Otherwise, we edit the original affinity for restoration later. 566 // Otherwise, we edit the original affinity for restoration later.
534 original_physical_ideal_core_id = cpu_core_id; 567 original_physical_ideal_core_id = core_id_;
535 original_physical_affinity_mask.SetAffinityMask(p_affinity_mask); 568 original_physical_affinity_mask.SetAffinityMask(p_affinity_mask);
536 } 569 }
537 } 570 }
538 571
539 // Update the pinned waiter list. 572 // Update the pinned waiter list.
573 ThreadQueueImplForKThreadSetProperty wait_queue_(kernel, std::addressof(pinned_waiter_list));
540 { 574 {
541 bool retry_update{}; 575 bool retry_update{};
542 bool thread_is_pinned{};
543 do { 576 do {
544 // Lock the scheduler. 577 // Lock the scheduler.
545 KScopedSchedulerLock sl{kernel}; 578 KScopedSchedulerLock sl(kernel);
546 579
547 // Don't do any further management if our termination has been requested. 580 // Don't do any further management if our termination has been requested.
548 R_SUCCEED_IF(IsTerminationRequested()); 581 R_SUCCEED_IF(IsTerminationRequested());
@@ -570,12 +603,9 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
570 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), 603 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
571 ResultTerminationRequested); 604 ResultTerminationRequested);
572 605
573 // Note that the thread was pinned.
574 thread_is_pinned = true;
575
576 // Wait until the thread isn't pinned any more. 606 // Wait until the thread isn't pinned any more.
577 pinned_waiter_list.push_back(GetCurrentThread(kernel)); 607 pinned_waiter_list.push_back(GetCurrentThread(kernel));
578 GetCurrentThread(kernel).SetState(ThreadState::Waiting); 608 GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
579 } else { 609 } else {
580 // If the thread isn't pinned, release the scheduler lock and retry until it's 610 // If the thread isn't pinned, release the scheduler lock and retry until it's
581 // not current. 611 // not current.
@@ -583,16 +613,6 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
583 } 613 }
584 } 614 }
585 } while (retry_update); 615 } while (retry_update);
586
587 // If the thread was pinned, it no longer is, and we should remove the current thread from
588 // our waiter list.
589 if (thread_is_pinned) {
590 // Lock the scheduler.
591 KScopedSchedulerLock sl{kernel};
592
593 // Remove from the list.
594 pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
595 }
596 } 616 }
597 617
598 return ResultSuccess; 618 return ResultSuccess;
@@ -641,15 +661,9 @@ void KThread::WaitCancel() {
641 KScopedSchedulerLock sl{kernel}; 661 KScopedSchedulerLock sl{kernel};
642 662
643 // Check if we're waiting and cancellable. 663 // Check if we're waiting and cancellable.
644 if (GetState() == ThreadState::Waiting && cancellable) { 664 if (this->GetState() == ThreadState::Waiting && cancellable) {
645 if (sleeping_queue != nullptr) { 665 wait_cancelled = false;
646 sleeping_queue->WakeupThread(this); 666 wait_queue->CancelWait(this, ResultCancelled, true);
647 wait_cancelled = true;
648 } else {
649 SetSyncedObject(nullptr, ResultCancelled);
650 SetState(ThreadState::Runnable);
651 wait_cancelled = false;
652 }
653 } else { 667 } else {
654 // Otherwise, note that we cancelled a wait. 668 // Otherwise, note that we cancelled a wait.
655 wait_cancelled = true; 669 wait_cancelled = true;
@@ -700,60 +714,59 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
700 // Set the activity. 714 // Set the activity.
701 { 715 {
702 // Lock the scheduler. 716 // Lock the scheduler.
703 KScopedSchedulerLock sl{kernel}; 717 KScopedSchedulerLock sl(kernel);
704 718
705 // Verify our state. 719 // Verify our state.
706 const auto cur_state = GetState(); 720 const auto cur_state = this->GetState();
707 R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable), 721 R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable),
708 ResultInvalidState); 722 ResultInvalidState);
709 723
710 // Either pause or resume. 724 // Either pause or resume.
711 if (activity == Svc::ThreadActivity::Paused) { 725 if (activity == Svc::ThreadActivity::Paused) {
712 // Verify that we're not suspended. 726 // Verify that we're not suspended.
713 R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState); 727 R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
714 728
715 // Suspend. 729 // Suspend.
716 RequestSuspend(SuspendType::Thread); 730 this->RequestSuspend(SuspendType::Thread);
717 } else { 731 } else {
718 ASSERT(activity == Svc::ThreadActivity::Runnable); 732 ASSERT(activity == Svc::ThreadActivity::Runnable);
719 733
720 // Verify that we're suspended. 734 // Verify that we're suspended.
721 R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState); 735 R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
722 736
723 // Resume. 737 // Resume.
724 Resume(SuspendType::Thread); 738 this->Resume(SuspendType::Thread);
725 } 739 }
726 } 740 }
727 741
728 // If the thread is now paused, update the pinned waiter list. 742 // If the thread is now paused, update the pinned waiter list.
729 if (activity == Svc::ThreadActivity::Paused) { 743 if (activity == Svc::ThreadActivity::Paused) {
730 bool thread_is_pinned{}; 744 ThreadQueueImplForKThreadSetProperty wait_queue_(kernel,
731 bool thread_is_current{}; 745 std::addressof(pinned_waiter_list));
746
747 bool thread_is_current;
732 do { 748 do {
733 // Lock the scheduler. 749 // Lock the scheduler.
734 KScopedSchedulerLock sl{kernel}; 750 KScopedSchedulerLock sl(kernel);
735 751
736 // Don't do any further management if our termination has been requested. 752 // Don't do any further management if our termination has been requested.
737 R_SUCCEED_IF(IsTerminationRequested()); 753 R_SUCCEED_IF(this->IsTerminationRequested());
754
755 // By default, treat the thread as not current.
756 thread_is_current = false;
738 757
739 // Check whether the thread is pinned. 758 // Check whether the thread is pinned.
740 if (GetStackParameters().is_pinned) { 759 if (this->GetStackParameters().is_pinned) {
741 // Verify that the current thread isn't terminating. 760 // Verify that the current thread isn't terminating.
742 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), 761 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
743 ResultTerminationRequested); 762 ResultTerminationRequested);
744 763
745 // Note that the thread was pinned and not current.
746 thread_is_pinned = true;
747 thread_is_current = false;
748
749 // Wait until the thread isn't pinned any more. 764 // Wait until the thread isn't pinned any more.
750 pinned_waiter_list.push_back(GetCurrentThread(kernel)); 765 pinned_waiter_list.push_back(GetCurrentThread(kernel));
751 GetCurrentThread(kernel).SetState(ThreadState::Waiting); 766 GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
752 } else { 767 } else {
753 // Check if the thread is currently running. 768 // Check if the thread is currently running.
754 // If it is, we'll need to retry. 769 // If it is, we'll need to retry.
755 thread_is_current = false;
756
757 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { 770 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
758 if (kernel.Scheduler(i).GetCurrentThread() == this) { 771 if (kernel.Scheduler(i).GetCurrentThread() == this) {
759 thread_is_current = true; 772 thread_is_current = true;
@@ -762,16 +775,6 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
762 } 775 }
763 } 776 }
764 } while (thread_is_current); 777 } while (thread_is_current);
765
766 // If the thread was pinned, it no longer is, and we should remove the current thread from
767 // our waiter list.
768 if (thread_is_pinned) {
769 // Lock the scheduler.
770 KScopedSchedulerLock sl{kernel};
771
772 // Remove from the list.
773 pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
774 }
775 } 778 }
776 779
777 return ResultSuccess; 780 return ResultSuccess;
@@ -966,6 +969,9 @@ ResultCode KThread::Run() {
966 969
967 // Set our state and finish. 970 // Set our state and finish.
968 SetState(ThreadState::Runnable); 971 SetState(ThreadState::Runnable);
972
973 DisableDispatch();
974
969 return ResultSuccess; 975 return ResultSuccess;
970 } 976 }
971} 977}
@@ -996,27 +1002,61 @@ ResultCode KThread::Sleep(s64 timeout) {
996 ASSERT(this == GetCurrentThreadPointer(kernel)); 1002 ASSERT(this == GetCurrentThreadPointer(kernel));
997 ASSERT(timeout > 0); 1003 ASSERT(timeout > 0);
998 1004
1005 ThreadQueueImplForKThreadSleep wait_queue_(kernel);
999 { 1006 {
1000 // Setup the scheduling lock and sleep. 1007 // Setup the scheduling lock and sleep.
1001 KScopedSchedulerLockAndSleep slp{kernel, this, timeout}; 1008 KScopedSchedulerLockAndSleep slp(kernel, this, timeout);
1002 1009
1003 // Check if the thread should terminate. 1010 // Check if the thread should terminate.
1004 if (IsTerminationRequested()) { 1011 if (this->IsTerminationRequested()) {
1005 slp.CancelSleep(); 1012 slp.CancelSleep();
1006 return ResultTerminationRequested; 1013 return ResultTerminationRequested;
1007 } 1014 }
1008 1015
1009 // Mark the thread as waiting. 1016 // Wait for the sleep to end.
1010 SetState(ThreadState::Waiting); 1017 this->BeginWait(std::addressof(wait_queue_));
1011 SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); 1018 SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
1012 } 1019 }
1013 1020
1014 // The lock/sleep is done. 1021 return ResultSuccess;
1022}
1015 1023
1016 // Cancel the timer. 1024void KThread::BeginWait(KThreadQueue* queue) {
1017 kernel.TimeManager().UnscheduleTimeEvent(this); 1025 // Set our state as waiting.
1026 SetState(ThreadState::Waiting);
1018 1027
1019 return ResultSuccess; 1028 // Set our wait queue.
1029 wait_queue = queue;
1030}
1031
1032void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
1033 // Lock the scheduler.
1034 KScopedSchedulerLock sl(kernel);
1035
1036 // If we're waiting, notify our queue that we're available.
1037 if (GetState() == ThreadState::Waiting) {
1038 wait_queue->NotifyAvailable(this, signaled_object, wait_result_);
1039 }
1040}
1041
1042void KThread::EndWait(ResultCode wait_result_) {
1043 // Lock the scheduler.
1044 KScopedSchedulerLock sl(kernel);
1045
1046 // If we're waiting, notify our queue that we're available.
1047 if (GetState() == ThreadState::Waiting) {
1048 wait_queue->EndWait(this, wait_result_);
1049 }
1050}
1051
1052void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
1053 // Lock the scheduler.
1054 KScopedSchedulerLock sl(kernel);
1055
1056 // If we're waiting, notify our queue that we're available.
1057 if (GetState() == ThreadState::Waiting) {
1058 wait_queue->CancelWait(this, wait_result_, cancel_timer_task);
1059 }
1020} 1060}
1021 1061
1022void KThread::SetState(ThreadState state) { 1062void KThread::SetState(ThreadState state) {
@@ -1050,4 +1090,26 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
1050 return GetCurrentThread(kernel).GetCurrentCore(); 1090 return GetCurrentThread(kernel).GetCurrentCore();
1051} 1091}
1052 1092
1093KScopedDisableDispatch::~KScopedDisableDispatch() {
1094 // If we are shutting down the kernel, none of this is relevant anymore.
1095 if (kernel.IsShuttingDown()) {
1096 return;
1097 }
1098
1099 // Skip the reschedule if single-core, as dispatch tracking is disabled here.
1100 if (!Settings::values.use_multi_core.GetValue()) {
1101 return;
1102 }
1103
1104 if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
1105 auto scheduler = kernel.CurrentScheduler();
1106
1107 if (scheduler) {
1108 scheduler->RescheduleCurrentCore();
1109 }
1110 } else {
1111 GetCurrentThread(kernel).EnableDispatch();
1112 }
1113}
1114
1053} // namespace Kernel 1115} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index c77f44ad4..c8a08bd71 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -48,6 +48,7 @@ enum class ThreadType : u32 {
48 Kernel = 1, 48 Kernel = 1,
49 HighPriority = 2, 49 HighPriority = 2,
50 User = 3, 50 User = 3,
51 Dummy = 100, // Special thread type for emulation purposes only
51}; 52};
52DECLARE_ENUM_FLAG_OPERATORS(ThreadType); 53DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
53 54
@@ -161,8 +162,6 @@ public:
161 } 162 }
162 } 163 }
163 164
164 void Wakeup();
165
166 void SetBasePriority(s32 value); 165 void SetBasePriority(s32 value);
167 166
168 [[nodiscard]] ResultCode Run(); 167 [[nodiscard]] ResultCode Run();
@@ -197,13 +196,19 @@ public:
197 196
198 void Suspend(); 197 void Suspend();
199 198
200 void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) { 199 constexpr void SetSyncedIndex(s32 index) {
201 synced_object = obj; 200 synced_index = index;
201 }
202
203 [[nodiscard]] constexpr s32 GetSyncedIndex() const {
204 return synced_index;
205 }
206
207 constexpr void SetWaitResult(ResultCode wait_res) {
202 wait_result = wait_res; 208 wait_result = wait_res;
203 } 209 }
204 210
205 [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const { 211 [[nodiscard]] constexpr ResultCode GetWaitResult() const {
206 *out = synced_object;
207 return wait_result; 212 return wait_result;
208 } 213 }
209 214
@@ -374,6 +379,8 @@ public:
374 379
375 [[nodiscard]] bool IsSignaled() const override; 380 [[nodiscard]] bool IsSignaled() const override;
376 381
382 void OnTimer();
383
377 static void PostDestroy(uintptr_t arg); 384 static void PostDestroy(uintptr_t arg);
378 385
379 [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); 386 [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
@@ -446,20 +453,39 @@ public:
446 return per_core_priority_queue_entry[core]; 453 return per_core_priority_queue_entry[core];
447 } 454 }
448 455
449 void SetSleepingQueue(KThreadQueue* q) { 456 [[nodiscard]] bool IsKernelThread() const {
450 sleeping_queue = q; 457 return GetActiveCore() == 3;
458 }
459
460 [[nodiscard]] bool IsDispatchTrackingDisabled() const {
461 return is_single_core || IsKernelThread();
451 } 462 }
452 463
453 [[nodiscard]] s32 GetDisableDispatchCount() const { 464 [[nodiscard]] s32 GetDisableDispatchCount() const {
465 if (IsDispatchTrackingDisabled()) {
466 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
467 return 1;
468 }
469
454 return this->GetStackParameters().disable_count; 470 return this->GetStackParameters().disable_count;
455 } 471 }
456 472
457 void DisableDispatch() { 473 void DisableDispatch() {
474 if (IsDispatchTrackingDisabled()) {
475 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
476 return;
477 }
478
458 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); 479 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
459 this->GetStackParameters().disable_count++; 480 this->GetStackParameters().disable_count++;
460 } 481 }
461 482
462 void EnableDispatch() { 483 void EnableDispatch() {
484 if (IsDispatchTrackingDisabled()) {
485 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
486 return;
487 }
488
463 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); 489 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
464 this->GetStackParameters().disable_count--; 490 this->GetStackParameters().disable_count--;
465 } 491 }
@@ -573,6 +599,15 @@ public:
573 address_key_value = val; 599 address_key_value = val;
574 } 600 }
575 601
602 void ClearWaitQueue() {
603 wait_queue = nullptr;
604 }
605
606 void BeginWait(KThreadQueue* queue);
607 void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
608 void EndWait(ResultCode wait_result_);
609 void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
610
576 [[nodiscard]] bool HasWaiters() const { 611 [[nodiscard]] bool HasWaiters() const {
577 return !waiter_list.empty(); 612 return !waiter_list.empty();
578 } 613 }
@@ -667,7 +702,6 @@ private:
667 KAffinityMask physical_affinity_mask{}; 702 KAffinityMask physical_affinity_mask{};
668 u64 thread_id{}; 703 u64 thread_id{};
669 std::atomic<s64> cpu_time{}; 704 std::atomic<s64> cpu_time{};
670 KSynchronizationObject* synced_object{};
671 VAddr address_key{}; 705 VAddr address_key{};
672 KProcess* parent{}; 706 KProcess* parent{};
673 VAddr kernel_stack_top{}; 707 VAddr kernel_stack_top{};
@@ -677,13 +711,14 @@ private:
677 s64 schedule_count{}; 711 s64 schedule_count{};
678 s64 last_scheduled_tick{}; 712 s64 last_scheduled_tick{};
679 std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; 713 std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
680 KThreadQueue* sleeping_queue{}; 714 KThreadQueue* wait_queue{};
681 WaiterList waiter_list{}; 715 WaiterList waiter_list{};
682 WaiterList pinned_waiter_list{}; 716 WaiterList pinned_waiter_list{};
683 KThread* lock_owner{}; 717 KThread* lock_owner{};
684 u32 address_key_value{}; 718 u32 address_key_value{};
685 u32 suspend_request_flags{}; 719 u32 suspend_request_flags{};
686 u32 suspend_allowed_flags{}; 720 u32 suspend_allowed_flags{};
721 s32 synced_index{};
687 ResultCode wait_result{ResultSuccess}; 722 ResultCode wait_result{ResultSuccess};
688 s32 base_priority{}; 723 s32 base_priority{};
689 s32 physical_ideal_core_id{}; 724 s32 physical_ideal_core_id{};
@@ -708,6 +743,7 @@ private:
708 743
709 // For emulation 744 // For emulation
710 std::shared_ptr<Common::Fiber> host_context{}; 745 std::shared_ptr<Common::Fiber> host_context{};
746 bool is_single_core{};
711 747
712 // For debugging 748 // For debugging
713 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 749 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
@@ -752,4 +788,20 @@ public:
752 } 788 }
753}; 789};
754 790
791class KScopedDisableDispatch {
792public:
793 [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
794 // If we are shutting down the kernel, none of this is relevant anymore.
795 if (kernel.IsShuttingDown()) {
796 return;
797 }
798 GetCurrentThread(kernel).DisableDispatch();
799 }
800
801 ~KScopedDisableDispatch();
802
803private:
804 KernelCore& kernel;
805};
806
755} // namespace Kernel 807} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp
new file mode 100644
index 000000000..d5248b547
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -0,0 +1,49 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/k_thread_queue.h"
6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/time_manager.h"
8
9namespace Kernel {
10
11void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
12 [[maybe_unused]] KSynchronizationObject* signaled_object,
13 [[maybe_unused]] ResultCode wait_result) {}
14
15void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
16 // Set the thread's wait result.
17 waiting_thread->SetWaitResult(wait_result);
18
19 // Set the thread as runnable.
20 waiting_thread->SetState(ThreadState::Runnable);
21
22 // Clear the thread's wait queue.
23 waiting_thread->ClearWaitQueue();
24
25 // Cancel the thread task.
26 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
27}
28
29void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
30 bool cancel_timer_task) {
31 // Set the thread's wait result.
32 waiting_thread->SetWaitResult(wait_result);
33
34 // Set the thread as runnable.
35 waiting_thread->SetState(ThreadState::Runnable);
36
37 // Clear the thread's wait queue.
38 waiting_thread->ClearWaitQueue();
39
40 // Cancel the thread task.
41 if (cancel_timer_task) {
42 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
43 }
44}
45
46void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
47 [[maybe_unused]] ResultCode wait_result) {}
48
49} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h
index 35d471dc5..ccb718e49 100644
--- a/src/core/hle/kernel/k_thread_queue.h
+++ b/src/core/hle/kernel/k_thread_queue.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_thread.h" 8#include "core/hle/kernel/k_thread.h"
8 9
9namespace Kernel { 10namespace Kernel {
@@ -11,71 +12,24 @@ namespace Kernel {
11class KThreadQueue { 12class KThreadQueue {
12public: 13public:
13 explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {} 14 explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {}
15 virtual ~KThreadQueue() = default;
14 16
15 bool IsEmpty() const { 17 virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
16 return wait_list.empty(); 18 ResultCode wait_result);
17 } 19 virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
18 20 virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
19 KThread::WaiterList::iterator begin() { 21 bool cancel_timer_task);
20 return wait_list.begin();
21 }
22 KThread::WaiterList::iterator end() {
23 return wait_list.end();
24 }
25
26 bool SleepThread(KThread* t) {
27 KScopedSchedulerLock sl{kernel};
28
29 // If the thread needs terminating, don't enqueue it.
30 if (t->IsTerminationRequested()) {
31 return false;
32 }
33
34 // Set the thread's queue and mark it as waiting.
35 t->SetSleepingQueue(this);
36 t->SetState(ThreadState::Waiting);
37
38 // Add the thread to the queue.
39 wait_list.push_back(*t);
40
41 return true;
42 }
43
44 void WakeupThread(KThread* t) {
45 KScopedSchedulerLock sl{kernel};
46
47 // Remove the thread from the queue.
48 wait_list.erase(wait_list.iterator_to(*t));
49
50 // Mark the thread as no longer sleeping.
51 t->SetState(ThreadState::Runnable);
52 t->SetSleepingQueue(nullptr);
53 }
54
55 KThread* WakeupFrontThread() {
56 KScopedSchedulerLock sl{kernel};
57
58 if (wait_list.empty()) {
59 return nullptr;
60 } else {
61 // Remove the thread from the queue.
62 auto it = wait_list.begin();
63 KThread* thread = std::addressof(*it);
64 wait_list.erase(it);
65
66 ASSERT(thread->GetState() == ThreadState::Waiting);
67
68 // Mark the thread as no longer sleeping.
69 thread->SetState(ThreadState::Runnable);
70 thread->SetSleepingQueue(nullptr);
71
72 return thread;
73 }
74 }
75 22
76private: 23private:
77 KernelCore& kernel; 24 KernelCore& kernel;
78 KThread::WaiterList wait_list{}; 25 KThread::WaiterList wait_list{};
79}; 26};
80 27
28class KThreadQueueWithoutEndWait : public KThreadQueue {
29public:
30 explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
31
32 void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
33};
34
81} // namespace Kernel 35} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e42a6d36f..2e4e4cb1c 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -14,6 +14,7 @@
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/scope_exit.h"
17#include "common/thread.h" 18#include "common/thread.h"
18#include "common/thread_worker.h" 19#include "common/thread_worker.h"
19#include "core/arm/arm_interface.h" 20#include "core/arm/arm_interface.h"
@@ -83,12 +84,16 @@ struct KernelCore::Impl {
83 } 84 }
84 85
85 void InitializeCores() { 86 void InitializeCores() {
86 for (auto& core : cores) { 87 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
87 core.Initialize(current_process->Is64BitProcess()); 88 cores[core_id].Initialize(current_process->Is64BitProcess());
89 system.Memory().SetCurrentPageTable(*current_process, core_id);
88 } 90 }
89 } 91 }
90 92
91 void Shutdown() { 93 void Shutdown() {
94 is_shutting_down.store(true, std::memory_order_relaxed);
95 SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
96
92 process_list.clear(); 97 process_list.clear();
93 98
94 // Close all open server ports. 99 // Close all open server ports.
@@ -123,15 +128,6 @@ struct KernelCore::Impl {
123 next_user_process_id = KProcess::ProcessIDMin; 128 next_user_process_id = KProcess::ProcessIDMin;
124 next_thread_id = 1; 129 next_thread_id = 1;
125 130
126 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
127 if (suspend_threads[core_id]) {
128 suspend_threads[core_id]->Close();
129 suspend_threads[core_id] = nullptr;
130 }
131
132 schedulers[core_id].reset();
133 }
134
135 cores.clear(); 131 cores.clear();
136 132
137 global_handle_table->Finalize(); 133 global_handle_table->Finalize();
@@ -159,6 +155,16 @@ struct KernelCore::Impl {
159 CleanupObject(time_shared_mem); 155 CleanupObject(time_shared_mem);
160 CleanupObject(system_resource_limit); 156 CleanupObject(system_resource_limit);
161 157
158 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
159 if (suspend_threads[core_id]) {
160 suspend_threads[core_id]->Close();
161 suspend_threads[core_id] = nullptr;
162 }
163
164 schedulers[core_id]->Finalize();
165 schedulers[core_id].reset();
166 }
167
162 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others 168 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
163 next_host_thread_id = Core::Hardware::NUM_CPU_CORES; 169 next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
164 170
@@ -245,13 +251,11 @@ struct KernelCore::Impl {
245 KScopedSchedulerLock lock(kernel); 251 KScopedSchedulerLock lock(kernel);
246 global_scheduler_context->PreemptThreads(); 252 global_scheduler_context->PreemptThreads();
247 } 253 }
248 const auto time_interval = std::chrono::nanoseconds{ 254 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
249 Core::Timing::msToCycles(std::chrono::milliseconds(10))};
250 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 255 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
251 }); 256 });
252 257
253 const auto time_interval = 258 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
254 std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))};
255 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 259 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
256 } 260 }
257 261
@@ -267,14 +271,6 @@ struct KernelCore::Impl {
267 271
268 void MakeCurrentProcess(KProcess* process) { 272 void MakeCurrentProcess(KProcess* process) {
269 current_process = process; 273 current_process = process;
270 if (process == nullptr) {
271 return;
272 }
273
274 const u32 core_id = GetCurrentHostThreadID();
275 if (core_id < Core::Hardware::NUM_CPU_CORES) {
276 system.Memory().SetCurrentPageTable(*process, core_id);
277 }
278 } 274 }
279 275
280 static inline thread_local u32 host_thread_id = UINT32_MAX; 276 static inline thread_local u32 host_thread_id = UINT32_MAX;
@@ -300,15 +296,16 @@ struct KernelCore::Impl {
300 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 296 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
301 KThread* GetHostDummyThread() { 297 KThread* GetHostDummyThread() {
302 auto make_thread = [this]() { 298 auto make_thread = [this]() {
303 std::unique_ptr<KThread> thread = std::make_unique<KThread>(system.Kernel()); 299 std::lock_guard lk(dummy_thread_lock);
300 auto& thread = dummy_threads.emplace_back(std::make_unique<KThread>(system.Kernel()));
304 KAutoObject::Create(thread.get()); 301 KAutoObject::Create(thread.get());
305 ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess()); 302 ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
306 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 303 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
307 return thread; 304 return thread.get();
308 }; 305 };
309 306
310 thread_local auto thread = make_thread(); 307 thread_local KThread* saved_thread = make_thread();
311 return thread.get(); 308 return saved_thread;
312 } 309 }
313 310
314 /// Registers a CPU core thread by allocating a host thread ID for it 311 /// Registers a CPU core thread by allocating a host thread ID for it
@@ -343,7 +340,16 @@ struct KernelCore::Impl {
343 is_phantom_mode_for_singlecore = value; 340 is_phantom_mode_for_singlecore = value;
344 } 341 }
345 342
343 bool IsShuttingDown() const {
344 return is_shutting_down.load(std::memory_order_relaxed);
345 }
346
346 KThread* GetCurrentEmuThread() { 347 KThread* GetCurrentEmuThread() {
348 // If we are shutting down the kernel, none of this is relevant anymore.
349 if (IsShuttingDown()) {
350 return {};
351 }
352
347 const auto thread_id = GetCurrentHostThreadID(); 353 const auto thread_id = GetCurrentHostThreadID();
348 if (thread_id >= Core::Hardware::NUM_CPU_CORES) { 354 if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
349 return GetHostDummyThread(); 355 return GetHostDummyThread();
@@ -695,6 +701,12 @@ struct KernelCore::Impl {
695 return port; 701 return port;
696 } 702 }
697 703
704 std::mutex server_ports_lock;
705 std::mutex server_sessions_lock;
706 std::mutex registered_objects_lock;
707 std::mutex registered_in_use_objects_lock;
708 std::mutex dummy_thread_lock;
709
698 std::atomic<u32> next_object_id{0}; 710 std::atomic<u32> next_object_id{0};
699 std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; 711 std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
700 std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; 712 std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
@@ -725,10 +737,6 @@ struct KernelCore::Impl {
725 std::unordered_set<KServerSession*> server_sessions; 737 std::unordered_set<KServerSession*> server_sessions;
726 std::unordered_set<KAutoObject*> registered_objects; 738 std::unordered_set<KAutoObject*> registered_objects;
727 std::unordered_set<KAutoObject*> registered_in_use_objects; 739 std::unordered_set<KAutoObject*> registered_in_use_objects;
728 std::mutex server_ports_lock;
729 std::mutex server_sessions_lock;
730 std::mutex registered_objects_lock;
731 std::mutex registered_in_use_objects_lock;
732 740
733 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; 741 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
734 std::vector<Kernel::PhysicalCore> cores; 742 std::vector<Kernel::PhysicalCore> cores;
@@ -753,7 +761,11 @@ struct KernelCore::Impl {
753 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; 761 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
754 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 762 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
755 763
764 // Specifically tracked to be automatically destroyed with kernel
765 std::vector<std::unique_ptr<KThread>> dummy_threads;
766
756 bool is_multicore{}; 767 bool is_multicore{};
768 std::atomic_bool is_shutting_down{};
757 bool is_phantom_mode_for_singlecore{}; 769 bool is_phantom_mode_for_singlecore{};
758 u32 single_core_thread_id{}; 770 u32 single_core_thread_id{};
759 771
@@ -839,16 +851,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
839 return impl->cores[id]; 851 return impl->cores[id];
840} 852}
841 853
854size_t KernelCore::CurrentPhysicalCoreIndex() const {
855 const u32 core_id = impl->GetCurrentHostThreadID();
856 if (core_id >= Core::Hardware::NUM_CPU_CORES) {
857 return Core::Hardware::NUM_CPU_CORES - 1;
858 }
859 return core_id;
860}
861
842Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { 862Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
843 u32 core_id = impl->GetCurrentHostThreadID(); 863 return impl->cores[CurrentPhysicalCoreIndex()];
844 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
845 return impl->cores[core_id];
846} 864}
847 865
848const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { 866const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
849 u32 core_id = impl->GetCurrentHostThreadID(); 867 return impl->cores[CurrentPhysicalCoreIndex()];
850 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
851 return impl->cores[core_id];
852} 868}
853 869
854Kernel::KScheduler* KernelCore::CurrentScheduler() { 870Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -1051,6 +1067,9 @@ void KernelCore::Suspend(bool in_suspention) {
1051 impl->suspend_threads[core_id]->SetState(state); 1067 impl->suspend_threads[core_id]->SetState(state);
1052 impl->suspend_threads[core_id]->SetWaitReasonForDebugging( 1068 impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
1053 ThreadWaitReasonForDebugging::Suspended); 1069 ThreadWaitReasonForDebugging::Suspended);
1070 if (!should_suspend) {
1071 impl->suspend_threads[core_id]->DisableDispatch();
1072 }
1054 } 1073 }
1055 } 1074 }
1056} 1075}
@@ -1059,19 +1078,21 @@ bool KernelCore::IsMulticore() const {
1059 return impl->is_multicore; 1078 return impl->is_multicore;
1060} 1079}
1061 1080
1081bool KernelCore::IsShuttingDown() const {
1082 return impl->IsShuttingDown();
1083}
1084
1062void KernelCore::ExceptionalExit() { 1085void KernelCore::ExceptionalExit() {
1063 exception_exited = true; 1086 exception_exited = true;
1064 Suspend(true); 1087 Suspend(true);
1065} 1088}
1066 1089
1067void KernelCore::EnterSVCProfile() { 1090void KernelCore::EnterSVCProfile() {
1068 std::size_t core = impl->GetCurrentHostThreadID(); 1091 impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
1069 impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
1070} 1092}
1071 1093
1072void KernelCore::ExitSVCProfile() { 1094void KernelCore::ExitSVCProfile() {
1073 std::size_t core = impl->GetCurrentHostThreadID(); 1095 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
1074 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
1075} 1096}
1076 1097
1077std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { 1098std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index d2ceae950..b9b423908 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -53,6 +53,7 @@ class KSharedMemoryInfo;
53class KThread; 53class KThread;
54class KTransferMemory; 54class KTransferMemory;
55class KWritableEvent; 55class KWritableEvent;
56class KCodeMemory;
56class PhysicalCore; 57class PhysicalCore;
57class ServiceThread; 58class ServiceThread;
58class Synchronization; 59class Synchronization;
@@ -148,6 +149,9 @@ public:
148 /// Gets the an instance of the respective physical CPU core. 149 /// Gets the an instance of the respective physical CPU core.
149 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; 150 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
150 151
152 /// Gets the current physical core index for the running host thread.
153 std::size_t CurrentPhysicalCoreIndex() const;
154
151 /// Gets the sole instance of the Scheduler at the current running core. 155 /// Gets the sole instance of the Scheduler at the current running core.
152 Kernel::KScheduler* CurrentScheduler(); 156 Kernel::KScheduler* CurrentScheduler();
153 157
@@ -271,6 +275,8 @@ public:
271 275
272 bool IsMulticore() const; 276 bool IsMulticore() const;
273 277
278 bool IsShuttingDown() const;
279
274 void EnterSVCProfile(); 280 void EnterSVCProfile();
275 281
276 void ExitSVCProfile(); 282 void ExitSVCProfile();
@@ -326,6 +332,8 @@ public:
326 return slab_heap_container->transfer_memory; 332 return slab_heap_container->transfer_memory;
327 } else if constexpr (std::is_same_v<T, KWritableEvent>) { 333 } else if constexpr (std::is_same_v<T, KWritableEvent>) {
328 return slab_heap_container->writeable_event; 334 return slab_heap_container->writeable_event;
335 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
336 return slab_heap_container->code_memory;
329 } 337 }
330 } 338 }
331 339
@@ -377,6 +385,7 @@ private:
377 KSlabHeap<KThread> thread; 385 KSlabHeap<KThread> thread;
378 KSlabHeap<KTransferMemory> transfer_memory; 386 KSlabHeap<KTransferMemory> transfer_memory;
379 KSlabHeap<KWritableEvent> writeable_event; 387 KSlabHeap<KWritableEvent> writeable_event;
388 KSlabHeap<KCodeMemory> code_memory;
380 }; 389 };
381 390
382 std::unique_ptr<SlabHeapContainer> slab_heap_container; 391 std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 6721b6276..03f3dec10 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -25,24 +25,27 @@ public:
25 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); 25 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
26 26
27private: 27private:
28 std::vector<std::thread> threads; 28 std::vector<std::jthread> threads;
29 std::queue<std::function<void()>> requests; 29 std::queue<std::function<void()>> requests;
30 std::mutex queue_mutex; 30 std::mutex queue_mutex;
31 std::condition_variable condition; 31 std::condition_variable_any condition;
32 const std::string service_name; 32 const std::string service_name;
33 bool stop{};
34}; 33};
35 34
36ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) 35ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
37 : service_name{name} { 36 : service_name{name} {
38 for (std::size_t i = 0; i < num_threads; ++i) 37 for (std::size_t i = 0; i < num_threads; ++i) {
39 threads.emplace_back([this, &kernel] { 38 threads.emplace_back([this, &kernel](std::stop_token stop_token) {
40 Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); 39 Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
41 40
42 // Wait for first request before trying to acquire a render context 41 // Wait for first request before trying to acquire a render context
43 { 42 {
44 std::unique_lock lock{queue_mutex}; 43 std::unique_lock lock{queue_mutex};
45 condition.wait(lock, [this] { return stop || !requests.empty(); }); 44 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
45 }
46
47 if (stop_token.stop_requested()) {
48 return;
46 } 49 }
47 50
48 kernel.RegisterHostThread(); 51 kernel.RegisterHostThread();
@@ -52,10 +55,16 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
52 55
53 { 56 {
54 std::unique_lock lock{queue_mutex}; 57 std::unique_lock lock{queue_mutex};
55 condition.wait(lock, [this] { return stop || !requests.empty(); }); 58 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
56 if (stop || requests.empty()) { 59
60 if (stop_token.stop_requested()) {
57 return; 61 return;
58 } 62 }
63
64 if (requests.empty()) {
65 continue;
66 }
67
59 task = std::move(requests.front()); 68 task = std::move(requests.front());
60 requests.pop(); 69 requests.pop();
61 } 70 }
@@ -63,6 +72,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
63 task(); 72 task();
64 } 73 }
65 }); 74 });
75 }
66} 76}
67 77
68void ServiceThread::Impl::QueueSyncRequest(KSession& session, 78void ServiceThread::Impl::QueueSyncRequest(KSession& session,
@@ -88,12 +98,9 @@ void ServiceThread::Impl::QueueSyncRequest(KSession& session,
88} 98}
89 99
90ServiceThread::Impl::~Impl() { 100ServiceThread::Impl::~Impl() {
91 {
92 std::unique_lock lock{queue_mutex};
93 stop = true;
94 }
95 condition.notify_all(); 101 condition.notify_all();
96 for (std::thread& thread : threads) { 102 for (auto& thread : threads) {
103 thread.request_stop();
97 thread.join(); 104 thread.join();
98 } 105 }
99} 106}
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index f9d99bc51..a9f7438ea 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -18,6 +18,7 @@
18#include "core/core_timing.h" 18#include "core/core_timing.h"
19#include "core/hle/kernel/k_client_port.h" 19#include "core/hle/kernel/k_client_port.h"
20#include "core/hle/kernel/k_client_session.h" 20#include "core/hle/kernel/k_client_session.h"
21#include "core/hle/kernel/k_code_memory.h"
21#include "core/hle/kernel/k_event.h" 22#include "core/hle/kernel/k_event.h"
22#include "core/hle/kernel/k_handle_table.h" 23#include "core/hle/kernel/k_handle_table.h"
23#include "core/hle/kernel/k_memory_block.h" 24#include "core/hle/kernel/k_memory_block.h"
@@ -31,6 +32,7 @@
31#include "core/hle/kernel/k_shared_memory.h" 32#include "core/hle/kernel/k_shared_memory.h"
32#include "core/hle/kernel/k_synchronization_object.h" 33#include "core/hle/kernel/k_synchronization_object.h"
33#include "core/hle/kernel/k_thread.h" 34#include "core/hle/kernel/k_thread.h"
35#include "core/hle/kernel/k_thread_queue.h"
34#include "core/hle/kernel/k_transfer_memory.h" 36#include "core/hle/kernel/k_transfer_memory.h"
35#include "core/hle/kernel/k_writable_event.h" 37#include "core/hle/kernel/k_writable_event.h"
36#include "core/hle/kernel/kernel.h" 38#include "core/hle/kernel/kernel.h"
@@ -307,26 +309,29 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
307 309
308/// Makes a blocking IPC call to an OS service. 310/// Makes a blocking IPC call to an OS service.
309static ResultCode SendSyncRequest(Core::System& system, Handle handle) { 311static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
310
311 auto& kernel = system.Kernel(); 312 auto& kernel = system.Kernel();
312 313
314 // Create the wait queue.
315 KThreadQueue wait_queue(kernel);
316
317 // Get the client session from its handle.
318 KScopedAutoObject session =
319 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
320 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
321
322 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
323
313 auto thread = kernel.CurrentScheduler()->GetCurrentThread(); 324 auto thread = kernel.CurrentScheduler()->GetCurrentThread();
314 { 325 {
315 KScopedSchedulerLock lock(kernel); 326 KScopedSchedulerLock lock(kernel);
316 thread->SetState(ThreadState::Waiting); 327
317 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); 328 // This is a synchronous request, so we should wait for our request to complete.
318 329 GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
319 { 330 GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
320 KScopedAutoObject session = 331 session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
321 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
322 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
323 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
324 session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
325 }
326 } 332 }
327 333
328 KSynchronizationObject* dummy{}; 334 return thread->GetWaitResult();
329 return thread->GetWaitResult(std::addressof(dummy));
330} 335}
331 336
332static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { 337static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -873,7 +878,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
873 const u64 thread_ticks = current_thread->GetCpuTime(); 878 const u64 thread_ticks = current_thread->GetCpuTime();
874 879
875 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); 880 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
876 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { 881 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
877 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; 882 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
878 } 883 }
879 884
@@ -887,7 +892,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
887 return ResultInvalidHandle; 892 return ResultInvalidHandle;
888 } 893 }
889 894
890 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id != system.CurrentCoreIndex()) { 895 if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
896 info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
891 LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id); 897 LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
892 return ResultInvalidCombination; 898 return ResultInvalidCombination;
893 } 899 }
@@ -1169,6 +1175,8 @@ static u32 GetCurrentProcessorNumber32(Core::System& system) {
1169 return GetCurrentProcessorNumber(system); 1175 return GetCurrentProcessorNumber(system);
1170} 1176}
1171 1177
1178namespace {
1179
1172constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { 1180constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
1173 switch (perm) { 1181 switch (perm) {
1174 case Svc::MemoryPermission::Read: 1182 case Svc::MemoryPermission::Read:
@@ -1179,10 +1187,40 @@ constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
1179 } 1187 }
1180} 1188}
1181 1189
1182constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) { 1190[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
1183 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare; 1191 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
1184} 1192}
1185 1193
1194constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
1195 switch (perm) {
1196 case Svc::MemoryPermission::None:
1197 case Svc::MemoryPermission::Read:
1198 case Svc::MemoryPermission::ReadWrite:
1199 case Svc::MemoryPermission::ReadExecute:
1200 return true;
1201 default:
1202 return false;
1203 }
1204}
1205
1206constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
1207 return perm == Svc::MemoryPermission::ReadWrite;
1208}
1209
1210constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1211 return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
1212}
1213
1214constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
1215 return perm == Svc::MemoryPermission::None;
1216}
1217
1218constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1219 return perm == Svc::MemoryPermission::None;
1220}
1221
1222} // Anonymous namespace
1223
1186static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, 1224static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
1187 u64 size, Svc::MemoryPermission map_perm) { 1225 u64 size, Svc::MemoryPermission map_perm) {
1188 LOG_TRACE(Kernel_SVC, 1226 LOG_TRACE(Kernel_SVC,
@@ -1262,6 +1300,223 @@ static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle,
1262 return UnmapSharedMemory(system, shmem_handle, address, size); 1300 return UnmapSharedMemory(system, shmem_handle, address, size);
1263} 1301}
1264 1302
1303static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle,
1304 VAddr address, u64 size, Svc::MemoryPermission perm) {
1305 LOG_TRACE(Kernel_SVC,
1306 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1307 process_handle, address, size, perm);
1308
1309 // Validate the address/size.
1310 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1311 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1312 R_UNLESS(size > 0, ResultInvalidSize);
1313 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1314
1315 // Validate the memory permission.
1316 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1317
1318 // Get the process from its handle.
1319 KScopedAutoObject process =
1320 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
1321 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
1322
1323 // Validate that the address is in range.
1324 auto& page_table = process->PageTable();
1325 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
1326
1327 // Set the memory permission.
1328 return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
1329}
1330
1331static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
1332 VAddr src_address, u64 size) {
1333 LOG_TRACE(Kernel_SVC,
1334 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1335 dst_address, process_handle, src_address, size);
1336
1337 // Validate the address/size.
1338 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
1339 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1340 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1341 R_UNLESS(size > 0, ResultInvalidSize);
1342 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1343 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1344
1345 // Get the processes.
1346 KProcess* dst_process = system.CurrentProcess();
1347 KScopedAutoObject src_process =
1348 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1349 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1350
1351 // Get the page tables.
1352 auto& dst_pt = dst_process->PageTable();
1353 auto& src_pt = src_process->PageTable();
1354
1355 // Validate that the mapping is in range.
1356 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1357 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1358 ResultInvalidMemoryRegion);
1359
1360 // Create a new page group.
1361 KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
1362 KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
1363
1364 // Map the group.
1365 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
1366 KMemoryPermission::UserReadWrite));
1367
1368 return ResultSuccess;
1369}
1370
1371static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
1372 VAddr src_address, u64 size) {
1373 LOG_TRACE(Kernel_SVC,
1374 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1375 dst_address, process_handle, src_address, size);
1376
1377 // Validate the address/size.
1378 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
1379 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1380 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1381 R_UNLESS(size > 0, ResultInvalidSize);
1382 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1383 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1384
1385 // Get the processes.
1386 KProcess* dst_process = system.CurrentProcess();
1387 KScopedAutoObject src_process =
1388 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1389 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1390
1391 // Get the page tables.
1392 auto& dst_pt = dst_process->PageTable();
1393 auto& src_pt = src_process->PageTable();
1394
1395 // Validate that the mapping is in range.
1396 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1397 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1398 ResultInvalidMemoryRegion);
1399
1400 // Unmap the memory.
1401 R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
1402
1403 return ResultSuccess;
1404}
1405
1406static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1407 LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
1408 static_cast<void*>(out), address, size);
1409 // Get kernel instance.
1410 auto& kernel = system.Kernel();
1411
1412 // Validate address / size.
1413 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1414 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1415 R_UNLESS(size > 0, ResultInvalidSize);
1416 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1417
1418 // Create the code memory.
1419
1420 KCodeMemory* code_mem = KCodeMemory::Create(kernel);
1421 R_UNLESS(code_mem != nullptr, ResultOutOfResource);
1422
1423 // Verify that the region is in range.
1424 R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
1425 ResultInvalidCurrentMemory);
1426
1427 // Initialize the code memory.
1428 R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
1429
1430 // Register the code memory.
1431 KCodeMemory::Register(kernel, code_mem);
1432
1433 // Add the code memory to the handle table.
1434 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
1435
1436 code_mem->Close();
1437
1438 return ResultSuccess;
1439}
1440
1441static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
1442 VAddr address, size_t size, Svc::MemoryPermission perm) {
1443
1444 LOG_TRACE(Kernel_SVC,
1445 "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
1446 "permission=0x{:X}",
1447 code_memory_handle, operation, address, size, perm);
1448
1449 // Validate the address / size.
1450 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1451 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1452 R_UNLESS(size > 0, ResultInvalidSize);
1453 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1454
1455 // Get the code memory from its handle.
1456 KScopedAutoObject code_mem =
1457 system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
1458 R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
1459
1460 // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
1461 // This enables homebrew usage of these SVCs for JIT.
1462
1463 // Perform the operation.
1464 switch (static_cast<CodeMemoryOperation>(operation)) {
1465 case CodeMemoryOperation::Map: {
1466 // Check that the region is in range.
1467 R_UNLESS(
1468 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1469 ResultInvalidMemoryRegion);
1470
1471 // Check the memory permission.
1472 R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1473
1474 // Map the memory.
1475 R_TRY(code_mem->Map(address, size));
1476 } break;
1477 case CodeMemoryOperation::Unmap: {
1478 // Check that the region is in range.
1479 R_UNLESS(
1480 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1481 ResultInvalidMemoryRegion);
1482
1483 // Check the memory permission.
1484 R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1485
1486 // Unmap the memory.
1487 R_TRY(code_mem->Unmap(address, size));
1488 } break;
1489 case CodeMemoryOperation::MapToOwner: {
1490 // Check that the region is in range.
1491 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1492 KMemoryState::GeneratedCode),
1493 ResultInvalidMemoryRegion);
1494
1495 // Check the memory permission.
1496 R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1497
1498 // Map the memory to its owner.
1499 R_TRY(code_mem->MapToOwner(address, size, perm));
1500 } break;
1501 case CodeMemoryOperation::UnmapFromOwner: {
1502 // Check that the region is in range.
1503 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1504 KMemoryState::GeneratedCode),
1505 ResultInvalidMemoryRegion);
1506
1507 // Check the memory permission.
1508 R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1509
1510 // Unmap the memory from its owner.
1511 R_TRY(code_mem->UnmapFromOwner(address, size));
1512 } break;
1513 default:
1514 return ResultInvalidEnumValue;
1515 }
1516
1517 return ResultSuccess;
1518}
1519
1265static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, 1520static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1266 VAddr page_info_address, Handle process_handle, 1521 VAddr page_info_address, Handle process_handle,
1267 VAddr address) { 1522 VAddr address) {
@@ -1459,10 +1714,14 @@ static void ExitProcess32(Core::System& system) {
1459 ExitProcess(system); 1714 ExitProcess(system);
1460} 1715}
1461 1716
1462static constexpr bool IsValidVirtualCoreId(int32_t core_id) { 1717namespace {
1718
1719constexpr bool IsValidVirtualCoreId(int32_t core_id) {
1463 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); 1720 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
1464} 1721}
1465 1722
1723} // Anonymous namespace
1724
1466/// Creates a new thread 1725/// Creates a new thread
1467static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, 1726static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
1468 VAddr stack_bottom, u32 priority, s32 core_id) { 1727 VAddr stack_bottom, u32 priority, s32 core_id) {
@@ -1846,7 +2105,9 @@ static ResultCode ResetSignal32(Core::System& system, Handle handle) {
1846 return ResetSignal(system, handle); 2105 return ResetSignal(system, handle);
1847} 2106}
1848 2107
1849static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { 2108namespace {
2109
2110constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
1850 switch (perm) { 2111 switch (perm) {
1851 case MemoryPermission::None: 2112 case MemoryPermission::None:
1852 case MemoryPermission::Read: 2113 case MemoryPermission::Read:
@@ -1857,6 +2118,8 @@ static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
1857 } 2118 }
1858} 2119}
1859 2120
2121} // Anonymous namespace
2122
1860/// Creates a TransferMemory object 2123/// Creates a TransferMemory object
1861static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, 2124static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
1862 MemoryPermission map_perm) { 2125 MemoryPermission map_perm) {
@@ -2548,8 +2811,8 @@ static const FunctionDef SVC_Table_64[] = {
2548 {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, 2811 {0x48, nullptr, "MapPhysicalMemoryUnsafe"},
2549 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, 2812 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
2550 {0x4A, nullptr, "SetUnsafeLimit"}, 2813 {0x4A, nullptr, "SetUnsafeLimit"},
2551 {0x4B, nullptr, "CreateCodeMemory"}, 2814 {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"},
2552 {0x4C, nullptr, "ControlCodeMemory"}, 2815 {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"},
2553 {0x4D, nullptr, "SleepSystem"}, 2816 {0x4D, nullptr, "SleepSystem"},
2554 {0x4E, nullptr, "ReadWriteRegister"}, 2817 {0x4E, nullptr, "ReadWriteRegister"},
2555 {0x4F, nullptr, "SetProcessActivity"}, 2818 {0x4F, nullptr, "SetProcessActivity"},
@@ -2588,9 +2851,9 @@ static const FunctionDef SVC_Table_64[] = {
2588 {0x70, nullptr, "CreatePort"}, 2851 {0x70, nullptr, "CreatePort"},
2589 {0x71, nullptr, "ManageNamedPort"}, 2852 {0x71, nullptr, "ManageNamedPort"},
2590 {0x72, nullptr, "ConnectToPort"}, 2853 {0x72, nullptr, "ConnectToPort"},
2591 {0x73, nullptr, "SetProcessMemoryPermission"}, 2854 {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
2592 {0x74, nullptr, "MapProcessMemory"}, 2855 {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"},
2593 {0x75, nullptr, "UnmapProcessMemory"}, 2856 {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"},
2594 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, 2857 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
2595 {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, 2858 {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
2596 {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, 2859 {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 6e62e656f..86255fe6d 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -73,6 +73,23 @@ void SvcWrap64(Core::System& system) {
73 .raw); 73 .raw);
74} 74}
75 75
76// Used by MapProcessMemory and UnmapProcessMemory
77template <ResultCode func(Core::System&, u64, u32, u64, u64)>
78void SvcWrap64(Core::System& system) {
79 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
80 Param(system, 2), Param(system, 3))
81 .raw);
82}
83
84// Used by ControlCodeMemory
85template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
86void SvcWrap64(Core::System& system) {
87 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
88 static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
89 static_cast<Svc::MemoryPermission>(Param(system, 4)))
90 .raw);
91}
92
76template <ResultCode func(Core::System&, u32*)> 93template <ResultCode func(Core::System&, u32*)>
77void SvcWrap64(Core::System& system) { 94void SvcWrap64(Core::System& system) {
78 u32 param = 0; 95 u32 param = 0;
@@ -301,6 +318,16 @@ void SvcWrap64(Core::System& system) {
301 FuncReturn(system, retval); 318 FuncReturn(system, retval);
302} 319}
303 320
321// Used by CreateCodeMemory
322template <ResultCode func(Core::System&, Handle*, u64, u64)>
323void SvcWrap64(Core::System& system) {
324 u32 param_1 = 0;
325 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
326
327 system.CurrentArmInterface().SetReg(1, param_1);
328 FuncReturn(system, retval);
329}
330
304template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> 331template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
305void SvcWrap64(Core::System& system) { 332void SvcWrap64(Core::System& system) {
306 u32 param_1 = 0; 333 u32 param_1 = 0;
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 8cd7279a3..aa985d820 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -5,6 +5,7 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_thread.h" 9#include "core/hle/kernel/k_thread.h"
9#include "core/hle/kernel/time_manager.h" 10#include "core/hle/kernel/time_manager.h"
10 11
@@ -15,7 +16,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
15 Core::Timing::CreateEvent("Kernel::TimeManagerCallback", 16 Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
16 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { 17 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
17 KThread* thread = reinterpret_cast<KThread*>(thread_handle); 18 KThread* thread = reinterpret_cast<KThread*>(thread_handle);
18 thread->Wakeup(); 19 {
20 KScopedSchedulerLock sl(system.Kernel());
21 thread->OnTimer();
22 }
19 }); 23 });
20} 24}
21 25
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index aee8d4f93..e60661fe1 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -30,6 +30,7 @@
30#include "core/hle/service/apm/apm_controller.h" 30#include "core/hle/service/apm/apm_controller.h"
31#include "core/hle/service/apm/apm_interface.h" 31#include "core/hle/service/apm/apm_interface.h"
32#include "core/hle/service/bcat/backend/backend.h" 32#include "core/hle/service/bcat/backend/backend.h"
33#include "core/hle/service/caps/caps.h"
33#include "core/hle/service/filesystem/filesystem.h" 34#include "core/hle/service/filesystem/filesystem.h"
34#include "core/hle/service/ns/ns.h" 35#include "core/hle/service/ns/ns.h"
35#include "core/hle/service/nvflinger/nvflinger.h" 36#include "core/hle/service/nvflinger/nvflinger.h"
@@ -298,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
298 {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, 299 {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
299 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, 300 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
300 {110, nullptr, "SetApplicationAlbumUserData"}, 301 {110, nullptr, "SetApplicationAlbumUserData"},
301 {120, nullptr, "SaveCurrentScreenshot"}, 302 {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
302 {130, nullptr, "SetRecordVolumeMuted"}, 303 {130, nullptr, "SetRecordVolumeMuted"},
303 {1000, nullptr, "GetDebugStorageChannel"}, 304 {1000, nullptr, "GetDebugStorageChannel"},
304 }; 305 };
@@ -579,6 +580,17 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestCo
579 rb.Push(ResultSuccess); 580 rb.Push(ResultSuccess);
580} 581}
581 582
583void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) {
584 IPC::RequestParser rp{ctx};
585
586 const auto album_report_option = rp.PopEnum<Capture::AlbumReportOption>();
587
588 LOG_WARNING(Service_AM, "(STUBBED) called. album_report_option={}", album_report_option);
589
590 IPC::ResponseBuilder rb{ctx, 2};
591 rb.Push(ResultSuccess);
592}
593
582AppletMessageQueue::AppletMessageQueue(Core::System& system) 594AppletMessageQueue::AppletMessageQueue(Core::System& system)
583 : service_context{system, "AppletMessageQueue"} { 595 : service_context{system, "AppletMessageQueue"} {
584 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); 596 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 202d20757..2a578aea5 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -151,6 +151,7 @@ private:
151 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 151 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
152 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 152 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
153 void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); 153 void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx);
154 void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx);
154 155
155 enum class ScreenshotPermission : u32 { 156 enum class ScreenshotPermission : u32 {
156 Inherit = 0, 157 Inherit = 0,
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index 2721679c1..d073f2210 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -10,6 +10,9 @@
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/frontend/applets/controller.h" 12#include "core/frontend/applets/controller.h"
13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_core.h"
15#include "core/hid/hid_types.h"
13#include "core/hle/result.h" 16#include "core/hle/result.h"
14#include "core/hle/service/am/am.h" 17#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applets/applet_controller.h" 18#include "core/hle/service/am/applets/applet_controller.h"
@@ -25,7 +28,7 @@ namespace Service::AM::Applets {
25static Core::Frontend::ControllerParameters ConvertToFrontendParameters( 28static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
26 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, 29 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
27 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { 30 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
28 HID::Controller_NPad::NpadStyleSet npad_style_set; 31 Core::HID::NpadStyleTag npad_style_set;
29 npad_style_set.raw = private_arg.style_set; 32 npad_style_set.raw = private_arg.style_set;
30 33
31 return { 34 return {
@@ -243,19 +246,11 @@ void Controller::Execute() {
243void Controller::ConfigurationComplete() { 246void Controller::ConfigurationComplete() {
244 ControllerSupportResultInfo result_info{}; 247 ControllerSupportResultInfo result_info{};
245 248
246 const auto& players = Settings::values.players.GetValue();
247
248 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. 249 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
249 // Otherwise, only count connected players from P1-P8. 250 // Otherwise, only count connected players from P1-P8.
250 result_info.player_count = 251 result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount();
251 is_single_mode 252
252 ? 1 253 result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
253 : static_cast<s8>(std::count_if(players.begin(), players.end() - 2,
254 [](const auto& player) { return player.connected; }));
255
256 result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance(
257 players.begin(), std::find_if(players.begin(), players.end(),
258 [](const auto& player) { return player.connected; })));
259 254
260 result_info.result = 0; 255 result_info.result = 0;
261 256
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index 0a34c4fc0..1a832505e 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -16,6 +16,10 @@ namespace Core {
16class System; 16class System;
17} 17}
18 18
19namespace Core::HID {
20enum class NpadStyleSet : u32;
21}
22
19namespace Service::AM::Applets { 23namespace Service::AM::Applets {
20 24
21using IdentificationColor = std::array<u8, 4>; 25using IdentificationColor = std::array<u8, 4>;
@@ -52,7 +56,7 @@ struct ControllerSupportArgPrivate {
52 bool flag_1{}; 56 bool flag_1{};
53 ControllerSupportMode mode{}; 57 ControllerSupportMode mode{};
54 ControllerSupportCaller caller{}; 58 ControllerSupportCaller caller{};
55 u32 style_set{}; 59 Core::HID::NpadStyleSet style_set{};
56 u32 joy_hold_type{}; 60 u32 joy_hold_type{};
57}; 61};
58static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, 62static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 7320b1c0f..134ac1ee2 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -231,7 +231,7 @@ void AppletManager::SetDefaultAppletFrontendSet() {
231void AppletManager::SetDefaultAppletsIfMissing() { 231void AppletManager::SetDefaultAppletsIfMissing() {
232 if (frontend.controller == nullptr) { 232 if (frontend.controller == nullptr) {
233 frontend.controller = 233 frontend.controller =
234 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager()); 234 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
235 } 235 }
236 236
237 if (frontend.error == nullptr) { 237 if (frontend.error == nullptr) {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 7da1f2969..981b6c996 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -96,7 +96,7 @@ private:
96 96
97 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, 97 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
98 std::vector<opus_int16>& output, u64* out_performance_time) const { 98 std::vector<opus_int16>& output, u64* out_performance_time) const {
99 const auto start_time = std::chrono::high_resolution_clock::now(); 99 const auto start_time = std::chrono::steady_clock::now();
100 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 100 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
101 if (sizeof(OpusPacketHeader) > input.size()) { 101 if (sizeof(OpusPacketHeader) > input.size()) {
102 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", 102 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
@@ -135,7 +135,7 @@ private:
135 return false; 135 return false;
136 } 136 }
137 137
138 const auto end_time = std::chrono::high_resolution_clock::now() - start_time; 138 const auto end_time = std::chrono::steady_clock::now() - start_time;
139 sample_count = out_sample_count; 139 sample_count = out_sample_count;
140 consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size); 140 consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
141 if (out_performance_time != nullptr) { 141 if (out_performance_time != nullptr) {
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h
index b18adcb9d..7254055e6 100644
--- a/src/core/hle/service/caps/caps.h
+++ b/src/core/hle/service/caps/caps.h
@@ -24,7 +24,7 @@ enum class AlbumImageOrientation {
24 Orientation3 = 3, 24 Orientation3 = 3,
25}; 25};
26 26
27enum class AlbumReportOption { 27enum class AlbumReportOption : s32 {
28 Disable = 0, 28 Disable = 0,
29 Enable = 1, 29 Enable = 1,
30}; 30};
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 68c9240ae..3c36f4085 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -17,10 +17,11 @@ namespace Service::Friend {
17 17
18class IFriendService final : public ServiceFramework<IFriendService> { 18class IFriendService final : public ServiceFramework<IFriendService> {
19public: 19public:
20 explicit IFriendService(Core::System& system_) : ServiceFramework{system_, "IFriendService"} { 20 explicit IFriendService(Core::System& system_)
21 : ServiceFramework{system_, "IFriendService"}, service_context{system, "IFriendService"} {
21 // clang-format off 22 // clang-format off
22 static const FunctionInfo functions[] = { 23 static const FunctionInfo functions[] = {
23 {0, nullptr, "GetCompletionEvent"}, 24 {0, &IFriendService::GetCompletionEvent, "GetCompletionEvent"},
24 {1, nullptr, "Cancel"}, 25 {1, nullptr, "Cancel"},
25 {10100, nullptr, "GetFriendListIds"}, 26 {10100, nullptr, "GetFriendListIds"},
26 {10101, &IFriendService::GetFriendList, "GetFriendList"}, 27 {10101, &IFriendService::GetFriendList, "GetFriendList"},
@@ -109,6 +110,12 @@ public:
109 // clang-format on 110 // clang-format on
110 111
111 RegisterHandlers(functions); 112 RegisterHandlers(functions);
113
114 completion_event = service_context.CreateEvent("IFriendService:CompletionEvent");
115 }
116
117 ~IFriendService() override {
118 service_context.CloseEvent(completion_event);
112 } 119 }
113 120
114private: 121private:
@@ -129,6 +136,14 @@ private:
129 }; 136 };
130 static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); 137 static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
131 138
139 void GetCompletionEvent(Kernel::HLERequestContext& ctx) {
140 LOG_DEBUG(Service_Friend, "called");
141
142 IPC::ResponseBuilder rb{ctx, 2, 1};
143 rb.Push(ResultSuccess);
144 rb.PushCopyObjects(completion_event->GetReadableEvent());
145 }
146
132 void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) { 147 void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) {
133 // This is safe to stub, as there should be no adverse consequences from reporting no 148 // This is safe to stub, as there should be no adverse consequences from reporting no
134 // blocked users. 149 // blocked users.
@@ -179,6 +194,10 @@ private:
179 rb.Push<u32>(0); // Friend count 194 rb.Push<u32>(0); // Friend count
180 // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" 195 // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
181 } 196 }
197
198 KernelHelpers::ServiceContext service_context;
199
200 Kernel::KEvent* completion_event;
182}; 201};
183 202
184class INotificationService final : public ServiceFramework<INotificationService> { 203class INotificationService final : public ServiceFramework<INotificationService> {
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index a08dc9758..b24d469cf 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -8,6 +8,7 @@
8#include "core/hle/service/glue/bgtc.h" 8#include "core/hle/service/glue/bgtc.h"
9#include "core/hle/service/glue/ectx.h" 9#include "core/hle/service/glue/ectx.h"
10#include "core/hle/service/glue/glue.h" 10#include "core/hle/service/glue/glue.h"
11#include "core/hle/service/glue/notif.h"
11 12
12namespace Service::Glue { 13namespace Service::Glue {
13 14
@@ -24,6 +25,9 @@ void InstallInterfaces(Core::System& system) {
24 25
25 // Error Context 26 // Error Context
26 std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager()); 27 std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager());
28
29 // Notification Services for application
30 std::make_shared<NOTIF_A>(system)->InstallAsService(system.ServiceManager());
27} 31}
28 32
29} // namespace Service::Glue 33} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp
new file mode 100644
index 000000000..c559ec9df
--- /dev/null
+++ b/src/core/hle/service/glue/notif.cpp
@@ -0,0 +1,44 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/ipc_helpers.h"
6#include "core/hle/service/glue/notif.h"
7
8namespace Service::Glue {
9
10NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {500, nullptr, "RegisterAlarmSetting"},
14 {510, nullptr, "UpdateAlarmSetting"},
15 {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
16 {530, nullptr, "LoadApplicationParameter"},
17 {540, nullptr, "DeleteAlarmSetting"},
18 {1000, &NOTIF_A::Initialize, "Initialize"},
19 };
20 // clang-format on
21
22 RegisterHandlers(functions);
23}
24
25NOTIF_A::~NOTIF_A() = default;
26
27void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
28 // Returns an array of AlarmSetting
29 constexpr s32 alarm_count = 0;
30
31 LOG_WARNING(Service_NOTIF, "(STUBBED) called");
32
33 IPC::ResponseBuilder rb{ctx, 3};
34 rb.Push(ResultSuccess);
35 rb.Push(alarm_count);
36}
37
38void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) {
39 LOG_WARNING(Service_NOTIF, "(STUBBED) called");
40 IPC::ResponseBuilder rb{ctx, 2};
41 rb.Push(ResultSuccess);
42}
43
44} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h
new file mode 100644
index 000000000..6ecf2015c
--- /dev/null
+++ b/src/core/hle/service/glue/notif.h
@@ -0,0 +1,25 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Glue {
14
15class NOTIF_A final : public ServiceFramework<NOTIF_A> {
16public:
17 explicit NOTIF_A(Core::System& system_);
18 ~NOTIF_A() override;
19
20private:
21 void ListAlarmSettings(Kernel::HLERequestContext& ctx);
22 void Initialize(Kernel::HLERequestContext& ctx);
23};
24
25} // namespace Service::Glue
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
index bda6e2557..f0f3105dc 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
@@ -4,13 +4,18 @@
4 4
5#include "common/settings.h" 5#include "common/settings.h"
6#include "core/core_timing.h" 6#include "core/core_timing.h"
7#include "core/hid/emulated_console.h"
8#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/controllers/console_sixaxis.h" 9#include "core/hle/service/hid/controllers/console_sixaxis.h"
8 10
9namespace Service::HID { 11namespace Service::HID {
10constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
11 13
12Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_) 14Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_)
13 : ControllerBase{system_} {} 15 : ControllerBase{hid_core_} {
16 console = hid_core.GetEmulatedConsole();
17}
18
14Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; 19Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
15 20
16void Controller_ConsoleSixAxis::OnInit() {} 21void Controller_ConsoleSixAxis::OnInit() {}
@@ -19,44 +24,31 @@ void Controller_ConsoleSixAxis::OnRelease() {}
19 24
20void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 25void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 26 std::size_t size) {
22 seven_six_axis.header.timestamp = core_timing.GetCPUTicks();
23 seven_six_axis.header.total_entry_count = 17;
24
25 if (!IsControllerActivated() || !is_transfer_memory_set) { 27 if (!IsControllerActivated() || !is_transfer_memory_set) {
26 seven_six_axis.header.entry_count = 0; 28 seven_sixaxis_lifo.buffer_count = 0;
27 seven_six_axis.header.last_entry_index = 0; 29 seven_sixaxis_lifo.buffer_tail = 0;
28 return; 30 return;
29 } 31 }
30 seven_six_axis.header.entry_count = 16;
31
32 const auto& last_entry =
33 seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
34 seven_six_axis.header.last_entry_index = (seven_six_axis.header.last_entry_index + 1) % 17;
35 auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
36 32
37 cur_entry.sampling_number = last_entry.sampling_number + 1; 33 const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
38 cur_entry.sampling_number2 = cur_entry.sampling_number; 34 next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
39 35
40 // Try to read sixaxis sensor states 36 // Try to read sixaxis sensor states
41 MotionDevice motion_device{}; 37 const auto motion_status = console->GetMotion();
42 const auto& device = motions[0]; 38
43 if (device) { 39 console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
44 std::tie(motion_device.accel, motion_device.gyro, motion_device.rotation,
45 motion_device.orientation, motion_device.quaternion) = device->GetStatus();
46 console_six_axis.is_seven_six_axis_sensor_at_rest = motion_device.gyro.Length2() < 0.0001f;
47 }
48 40
49 cur_entry.accel = motion_device.accel; 41 next_seven_sixaxis_state.accel = motion_status.accel;
50 // Zero gyro values as they just mess up with the camera 42 // Zero gyro values as they just mess up with the camera
51 // Note: Probably a correct sensivity setting must be set 43 // Note: Probably a correct sensivity setting must be set
52 cur_entry.gyro = {}; 44 next_seven_sixaxis_state.gyro = {};
53 cur_entry.quaternion = { 45 next_seven_sixaxis_state.quaternion = {
54 { 46 {
55 motion_device.quaternion.xyz.y, 47 motion_status.quaternion.xyz.y,
56 motion_device.quaternion.xyz.x, 48 motion_status.quaternion.xyz.x,
57 -motion_device.quaternion.w, 49 -motion_status.quaternion.w,
58 }, 50 },
59 -motion_device.quaternion.xyz.z, 51 -motion_status.quaternion.xyz.z,
60 }; 52 };
61 53
62 console_six_axis.sampling_number++; 54 console_six_axis.sampling_number++;
@@ -67,14 +59,8 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
67 // Update console six axis shared memory 59 // Update console six axis shared memory
68 std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis)); 60 std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
69 // Update seven six axis transfer memory 61 // Update seven six axis transfer memory
70 std::memcpy(transfer_memory, &seven_six_axis, sizeof(seven_six_axis)); 62 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
71} 63 std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo));
72
73void Controller_ConsoleSixAxis::OnLoadInputDevices() {
74 const auto player = Settings::values.players.GetValue()[0];
75 std::transform(player.motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
76 player.motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions.begin(),
77 Input::CreateDevice<Input::MotionDevice>);
78} 64}
79 65
80void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) { 66void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
@@ -83,8 +69,7 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
83} 69}
84 70
85void Controller_ConsoleSixAxis::ResetTimestamp() { 71void Controller_ConsoleSixAxis::ResetTimestamp() {
86 auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index]; 72 seven_sixaxis_lifo.buffer_count = 0;
87 cur_entry.sampling_number = 0; 73 seven_sixaxis_lifo.buffer_tail = 0;
88 cur_entry.sampling_number2 = 0;
89} 74}
90} // namespace Service::HID 75} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h
index fd8a427af..279241858 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.h
@@ -5,16 +5,21 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h" 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/quaternion.h" 10#include "common/quaternion.h"
11#include "core/frontend/input.h" 11#include "core/hid/hid_types.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedConsole;
17} // namespace Core::HID
13 18
14namespace Service::HID { 19namespace Service::HID {
15class Controller_ConsoleSixAxis final : public ControllerBase { 20class Controller_ConsoleSixAxis final : public ControllerBase {
16public: 21public:
17 explicit Controller_ConsoleSixAxis(Core::System& system_); 22 explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
18 ~Controller_ConsoleSixAxis() override; 23 ~Controller_ConsoleSixAxis() override;
19 24
20 // Called when the controller is initialized 25 // Called when the controller is initialized
@@ -26,9 +31,6 @@ public:
26 // When the controller is requesting an update for the shared memory 31 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; 32 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
28 33
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32 // Called on InitializeSevenSixAxisSensor 34 // Called on InitializeSevenSixAxisSensor
33 void SetTransferMemoryPointer(u8* t_mem); 35 void SetTransferMemoryPointer(u8* t_mem);
34 36
@@ -38,43 +40,31 @@ public:
38private: 40private:
39 struct SevenSixAxisState { 41 struct SevenSixAxisState {
40 INSERT_PADDING_WORDS(4); // unused 42 INSERT_PADDING_WORDS(4); // unused
41 s64_le sampling_number{}; 43 s64 sampling_number{};
42 s64_le sampling_number2{};
43 u64 unknown{}; 44 u64 unknown{};
44 Common::Vec3f accel{}; 45 Common::Vec3f accel{};
45 Common::Vec3f gyro{}; 46 Common::Vec3f gyro{};
46 Common::Quaternion<f32> quaternion{}; 47 Common::Quaternion<f32> quaternion{};
47 }; 48 };
48 static_assert(sizeof(SevenSixAxisState) == 0x50, "SevenSixAxisState is an invalid size"); 49 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
49
50 struct SevenSixAxisMemory {
51 CommonHeader header{};
52 std::array<SevenSixAxisState, 0x21> sevensixaxis_states{};
53 };
54 static_assert(sizeof(SevenSixAxisMemory) == 0xA70, "SevenSixAxisMemory is an invalid size");
55 50
51 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
56 struct ConsoleSharedMemory { 52 struct ConsoleSharedMemory {
57 u64_le sampling_number{}; 53 u64 sampling_number{};
58 bool is_seven_six_axis_sensor_at_rest{}; 54 bool is_seven_six_axis_sensor_at_rest{};
55 INSERT_PADDING_BYTES(4); // padding
59 f32 verticalization_error{}; 56 f32 verticalization_error{};
60 Common::Vec3f gyro_bias{}; 57 Common::Vec3f gyro_bias{};
61 }; 58 };
62 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); 59 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
63 60
64 struct MotionDevice { 61 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
65 Common::Vec3f accel; 62 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
66 Common::Vec3f gyro;
67 Common::Vec3f rotation;
68 std::array<Common::Vec3f, 3> orientation;
69 Common::Quaternion<f32> quaternion;
70 };
71 63
72 using MotionArray = 64 Core::HID::EmulatedConsole* console;
73 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>;
74 MotionArray motions;
75 u8* transfer_memory = nullptr; 65 u8* transfer_memory = nullptr;
76 bool is_transfer_memory_set = false; 66 bool is_transfer_memory_set = false;
77 ConsoleSharedMemory console_six_axis{}; 67 ConsoleSharedMemory console_six_axis{};
78 SevenSixAxisMemory seven_six_axis{}; 68 SevenSixAxisState next_seven_sixaxis_state{};
79}; 69};
80} // namespace Service::HID 70} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 9d1e6db6a..788ae9ae7 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -6,12 +6,12 @@
6 6
7namespace Service::HID { 7namespace Service::HID {
8 8
9ControllerBase::ControllerBase(Core::System& system_) : system(system_) {} 9ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
10ControllerBase::~ControllerBase() = default; 10ControllerBase::~ControllerBase() = default;
11 11
12void ControllerBase::ActivateController() { 12void ControllerBase::ActivateController() {
13 if (is_activated) { 13 if (is_activated) {
14 OnRelease(); 14 return;
15 } 15 }
16 is_activated = true; 16 is_activated = true;
17 OnInit(); 17 OnInit();
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 1556fb08e..7450eb20a 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -11,14 +11,14 @@ namespace Core::Timing {
11class CoreTiming; 11class CoreTiming;
12} 12}
13 13
14namespace Core { 14namespace Core::HID {
15class System; 15class HIDCore;
16} 16}
17 17
18namespace Service::HID { 18namespace Service::HID {
19class ControllerBase { 19class ControllerBase {
20public: 20public:
21 explicit ControllerBase(Core::System& system_); 21 explicit ControllerBase(Core::HID::HIDCore& hid_core_);
22 virtual ~ControllerBase(); 22 virtual ~ControllerBase();
23 23
24 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -35,26 +35,17 @@ public:
35 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 35 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
36 std::size_t size) {} 36 std::size_t size) {}
37 37
38 // Called when input devices should be loaded
39 virtual void OnLoadInputDevices() = 0;
40
41 void ActivateController(); 38 void ActivateController();
42 39
43 void DeactivateController(); 40 void DeactivateController();
44 41
45 bool IsControllerActivated() const; 42 bool IsControllerActivated() const;
46 43
44 static const std::size_t hid_entry_count = 17;
45
47protected: 46protected:
48 bool is_activated{false}; 47 bool is_activated{false};
49 48
50 struct CommonHeader { 49 Core::HID::HIDCore& hid_core;
51 s64_le timestamp;
52 s64_le total_entry_count;
53 s64_le last_entry_index;
54 s64_le entry_count;
55 };
56 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
57
58 Core::System& system;
59}; 50};
60} // namespace Service::HID 51} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index d439b8fb0..6a6fb9cab 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,15 +6,19 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
11#include "core/hid/hid_types.h"
9#include "core/hle/service/hid/controllers/debug_pad.h" 12#include "core/hle/service/hid/controllers/debug_pad.h"
10 13
11namespace Service::HID { 14namespace Service::HID {
15constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
12 16
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 17Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_)
14[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 18 : ControllerBase{hid_core_} {
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; 19 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
20}
16 21
17Controller_DebugPad::Controller_DebugPad(Core::System& system_) : ControllerBase{system_} {}
18Controller_DebugPad::~Controller_DebugPad() = default; 22Controller_DebugPad::~Controller_DebugPad() = default;
19 23
20void Controller_DebugPad::OnInit() {} 24void Controller_DebugPad::OnInit() {}
@@ -23,63 +27,29 @@ void Controller_DebugPad::OnRelease() {}
23 27
24void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 28void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
25 std::size_t size) { 29 std::size_t size) {
26 shared_memory.header.timestamp = core_timing.GetCPUTicks();
27 shared_memory.header.total_entry_count = 17;
28
29 if (!IsControllerActivated()) { 30 if (!IsControllerActivated()) {
30 shared_memory.header.entry_count = 0; 31 debug_pad_lifo.buffer_count = 0;
31 shared_memory.header.last_entry_index = 0; 32 debug_pad_lifo.buffer_tail = 0;
33 std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
32 return; 34 return;
33 } 35 }
34 shared_memory.header.entry_count = 16;
35 36
36 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; 37 const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state;
37 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 38 next_state.sampling_number = last_entry.sampling_number + 1;
38 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
39
40 cur_entry.sampling_number = last_entry.sampling_number + 1;
41 cur_entry.sampling_number2 = cur_entry.sampling_number;
42 39
43 if (Settings::values.debug_pad_enabled) { 40 if (Settings::values.debug_pad_enabled) {
44 cur_entry.attribute.connected.Assign(1); 41 next_state.attribute.connected.Assign(1);
45 auto& pad = cur_entry.pad_state;
46 42
47 using namespace Settings::NativeButton; 43 const auto& button_state = controller->GetDebugPadButtons();
48 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); 44 const auto& stick_state = controller->GetSticks();
49 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
50 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
51 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
52 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
53 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
54 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
55 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
56 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
57 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
59 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
60 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
61 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
62 45
63 const auto [stick_l_x_f, stick_l_y_f] = 46 next_state.pad_state = button_state;
64 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 47 next_state.l_stick = stick_state.left;
65 const auto [stick_r_x_f, stick_r_y_f] = 48 next_state.r_stick = stick_state.right;
66 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
67 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
68 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
69 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
70 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
71 } 49 }
72 50
73 std::memcpy(data, &shared_memory, sizeof(SharedMemory)); 51 debug_pad_lifo.WriteNextEntry(next_state);
52 std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
74} 53}
75 54
76void Controller_DebugPad::OnLoadInputDevices() {
77 std::transform(Settings::values.debug_pad_buttons.begin(),
78 Settings::values.debug_pad_buttons.begin() +
79 Settings::NativeButton::NUM_BUTTONS_HID,
80 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
81 std::transform(Settings::values.debug_pad_analogs.begin(),
82 Settings::values.debug_pad_analogs.end(), analogs.begin(),
83 Input::CreateDevice<Input::AnalogDevice>);
84}
85} // namespace Service::HID 55} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 1b1645184..afe374fc2 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -8,15 +8,20 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
12#include "common/swap.h" 11#include "common/swap.h"
13#include "core/frontend/input.h"
14#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedController;
17struct DebugPadButton;
18struct AnalogStickState;
19} // namespace Core::HID
15 20
16namespace Service::HID { 21namespace Service::HID {
17class Controller_DebugPad final : public ControllerBase { 22class Controller_DebugPad final : public ControllerBase {
18public: 23public:
19 explicit Controller_DebugPad(Core::System& system_); 24 explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_);
20 ~Controller_DebugPad() override; 25 ~Controller_DebugPad() override;
21 26
22 // Called when the controller is initialized 27 // Called when the controller is initialized
@@ -28,66 +33,31 @@ public:
28 // When the controller is requesting an update for the shared memory 33 // When the controller is requesting an update for the shared memory
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 34 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
30 35
31 // Called when input devices should be loaded
32 void OnLoadInputDevices() override;
33
34private: 36private:
35 struct AnalogStick { 37 // This is nn::hid::DebugPadAttribute
36 s32_le x; 38 struct DebugPadAttribute {
37 s32_le y;
38 };
39 static_assert(sizeof(AnalogStick) == 0x8);
40
41 struct PadState {
42 union {
43 u32_le raw{};
44 BitField<0, 1, u32> a;
45 BitField<1, 1, u32> b;
46 BitField<2, 1, u32> x;
47 BitField<3, 1, u32> y;
48 BitField<4, 1, u32> l;
49 BitField<5, 1, u32> r;
50 BitField<6, 1, u32> zl;
51 BitField<7, 1, u32> zr;
52 BitField<8, 1, u32> plus;
53 BitField<9, 1, u32> minus;
54 BitField<10, 1, u32> d_left;
55 BitField<11, 1, u32> d_up;
56 BitField<12, 1, u32> d_right;
57 BitField<13, 1, u32> d_down;
58 };
59 };
60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
61
62 struct Attributes {
63 union { 39 union {
64 u32_le raw{}; 40 u32 raw{};
65 BitField<0, 1, u32> connected; 41 BitField<0, 1, u32> connected;
66 }; 42 };
67 }; 43 };
68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); 44 static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
69 45
70 struct PadStates { 46 // This is nn::hid::DebugPadState
71 s64_le sampling_number; 47 struct DebugPadState {
72 s64_le sampling_number2; 48 s64 sampling_number;
73 Attributes attribute; 49 DebugPadAttribute attribute;
74 PadState pad_state; 50 Core::HID::DebugPadButton pad_state;
75 AnalogStick r_stick; 51 Core::HID::AnalogStickState r_stick;
76 AnalogStick l_stick; 52 Core::HID::AnalogStickState l_stick;
77 }; 53 };
78 static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state"); 54 static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
79 55
80 struct SharedMemory { 56 // This is nn::hid::detail::DebugPadLifo
81 CommonHeader header; 57 Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
82 std::array<PadStates, 17> pad_states; 58 static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
83 INSERT_PADDING_BYTES(0x138); 59 DebugPadState next_state{};
84 };
85 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
86 SharedMemory shared_memory{};
87 60
88 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 61 Core::HID::EmulatedController* controller;
89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs;
92}; 62};
93} // namespace Service::HID 63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 764abb5b6..fe895c4f6 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -7,6 +7,7 @@
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
10#include "core/hid/hid_core.h"
10#include "core/hle/service/hid/controllers/gesture.h" 11#include "core/hle/service/hid/controllers/gesture.h"
11 12
12namespace Service::HID { 13namespace Service::HID {
@@ -23,16 +24,14 @@ constexpr f32 Square(s32 num) {
23 return static_cast<f32>(num * num); 24 return static_cast<f32>(num * num);
24} 25}
25 26
26Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {} 27Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
28 console = hid_core.GetEmulatedConsole();
29}
27Controller_Gesture::~Controller_Gesture() = default; 30Controller_Gesture::~Controller_Gesture() = default;
28 31
29void Controller_Gesture::OnInit() { 32void Controller_Gesture::OnInit() {
30 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 33 gesture_lifo.buffer_count = 0;
31 mouse_finger_id[id] = MAX_POINTS; 34 gesture_lifo.buffer_tail = 0;
32 keyboard_finger_id[id] = MAX_POINTS;
33 udp_finger_id[id] = MAX_POINTS;
34 }
35 shared_memory.header.entry_count = 0;
36 force_update = true; 35 force_update = true;
37} 36}
38 37
@@ -40,50 +39,38 @@ void Controller_Gesture::OnRelease() {}
40 39
41void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 40void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
42 std::size_t size) { 41 std::size_t size) {
43 shared_memory.header.timestamp = core_timing.GetCPUTicks();
44 shared_memory.header.total_entry_count = 17;
45
46 if (!IsControllerActivated()) { 42 if (!IsControllerActivated()) {
47 shared_memory.header.entry_count = 0; 43 gesture_lifo.buffer_count = 0;
48 shared_memory.header.last_entry_index = 0; 44 gesture_lifo.buffer_tail = 0;
45 std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
49 return; 46 return;
50 } 47 }
51 48
52 ReadTouchInput(); 49 ReadTouchInput();
53 50
54 GestureProperties gesture = GetGestureProperties(); 51 GestureProperties gesture = GetGestureProperties();
55 f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) / 52 f32 time_difference =
56 (1000 * 1000 * 1000); 53 static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
57 54
58 // Only update if necesary 55 // Only update if necesary
59 if (!ShouldUpdateGesture(gesture, time_difference)) { 56 if (!ShouldUpdateGesture(gesture, time_difference)) {
60 return; 57 return;
61 } 58 }
62 59
63 last_update_timestamp = shared_memory.header.timestamp; 60 last_update_timestamp = gesture_lifo.timestamp;
64 UpdateGestureSharedMemory(data, size, gesture, time_difference); 61 UpdateGestureSharedMemory(data, size, gesture, time_difference);
65} 62}
66 63
67void Controller_Gesture::ReadTouchInput() { 64void Controller_Gesture::ReadTouchInput() {
68 const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); 65 const auto touch_status = console->GetTouch();
69 const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); 66 for (std::size_t id = 0; id < fingers.size(); ++id) {
70 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 67 fingers[id] = touch_status[id];
71 mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
72 udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
73 }
74
75 if (Settings::values.use_touch_from_button) {
76 const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
77 for (std::size_t id = 0; id < mouse_status.size(); ++id) {
78 keyboard_finger_id[id] =
79 UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
80 }
81 } 68 }
82} 69}
83 70
84bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, 71bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
85 f32 time_difference) { 72 f32 time_difference) {
86 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 73 const auto& last_entry = GetLastGestureEntry();
87 if (force_update) { 74 if (force_update) {
88 force_update = false; 75 force_update = false;
89 return true; 76 return true;
@@ -97,7 +84,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
97 } 84 }
98 85
99 // Update on press and hold event after 0.5 seconds 86 // Update on press and hold event after 0.5 seconds
100 if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 && 87 if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
101 time_difference > press_delay) { 88 time_difference > press_delay) {
102 return enable_press_and_tap; 89 return enable_press_and_tap;
103 } 90 }
@@ -108,27 +95,19 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
108void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, 95void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
109 GestureProperties& gesture, 96 GestureProperties& gesture,
110 f32 time_difference) { 97 f32 time_difference) {
111 TouchType type = TouchType::Idle; 98 GestureType type = GestureType::Idle;
112 Attribute attributes{}; 99 GestureAttribute attributes{};
113 100
114 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 101 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
115 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
116 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
117 102
118 if (shared_memory.header.entry_count < 16) { 103 // Reset next state to default
119 shared_memory.header.entry_count++; 104 next_state.sampling_number = last_entry.sampling_number + 1;
120 } 105 next_state.delta = {};
121 106 next_state.vel_x = 0;
122 cur_entry.sampling_number = last_entry.sampling_number + 1; 107 next_state.vel_y = 0;
123 cur_entry.sampling_number2 = cur_entry.sampling_number; 108 next_state.direction = GestureDirection::None;
124 109 next_state.rotation_angle = 0;
125 // Reset values to default 110 next_state.scale = 0;
126 cur_entry.delta = {};
127 cur_entry.vel_x = 0;
128 cur_entry.vel_y = 0;
129 cur_entry.direction = Direction::None;
130 cur_entry.rotation_angle = 0;
131 cur_entry.scale = 0;
132 111
133 if (gesture.active_points > 0) { 112 if (gesture.active_points > 0) {
134 if (last_gesture.active_points == 0) { 113 if (last_gesture.active_points == 0) {
@@ -141,46 +120,47 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
141 } 120 }
142 121
143 // Apply attributes 122 // Apply attributes
144 cur_entry.detection_count = gesture.detection_count; 123 next_state.detection_count = gesture.detection_count;
145 cur_entry.type = type; 124 next_state.type = type;
146 cur_entry.attributes = attributes; 125 next_state.attributes = attributes;
147 cur_entry.pos = gesture.mid_point; 126 next_state.pos = gesture.mid_point;
148 cur_entry.point_count = static_cast<s32>(gesture.active_points); 127 next_state.point_count = static_cast<s32>(gesture.active_points);
149 cur_entry.points = gesture.points; 128 next_state.points = gesture.points;
150 last_gesture = gesture; 129 last_gesture = gesture;
151 130
152 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 131 gesture_lifo.WriteNextEntry(next_state);
132 std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
153} 133}
154 134
155void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type, 135void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
156 Attribute& attributes) { 136 GestureAttribute& attributes) {
157 const auto& last_entry = GetLastGestureEntry(); 137 const auto& last_entry = GetLastGestureEntry();
158 138
159 gesture.detection_count++; 139 gesture.detection_count++;
160 type = TouchType::Touch; 140 type = GestureType::Touch;
161 141
162 // New touch after cancel is not considered new 142 // New touch after cancel is not considered new
163 if (last_entry.type != TouchType::Cancel) { 143 if (last_entry.type != GestureType::Cancel) {
164 attributes.is_new_touch.Assign(1); 144 attributes.is_new_touch.Assign(1);
165 enable_press_and_tap = true; 145 enable_press_and_tap = true;
166 } 146 }
167} 147}
168 148
169void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type, 149void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
170 f32 time_difference) { 150 f32 time_difference) {
171 const auto& last_entry = GetLastGestureEntry(); 151 const auto& last_entry = GetLastGestureEntry();
172 152
173 // Promote to pan type if touch moved 153 // Promote to pan type if touch moved
174 for (size_t id = 0; id < MAX_POINTS; id++) { 154 for (size_t id = 0; id < MAX_POINTS; id++) {
175 if (gesture.points[id] != last_gesture.points[id]) { 155 if (gesture.points[id] != last_gesture.points[id]) {
176 type = TouchType::Pan; 156 type = GestureType::Pan;
177 break; 157 break;
178 } 158 }
179 } 159 }
180 160
181 // Number of fingers changed cancel the last event and clear data 161 // Number of fingers changed cancel the last event and clear data
182 if (gesture.active_points != last_gesture.active_points) { 162 if (gesture.active_points != last_gesture.active_points) {
183 type = TouchType::Cancel; 163 type = GestureType::Cancel;
184 enable_press_and_tap = false; 164 enable_press_and_tap = false;
185 gesture.active_points = 0; 165 gesture.active_points = 0;
186 gesture.mid_point = {}; 166 gesture.mid_point = {};
@@ -189,41 +169,41 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch
189 } 169 }
190 170
191 // Calculate extra parameters of panning 171 // Calculate extra parameters of panning
192 if (type == TouchType::Pan) { 172 if (type == GestureType::Pan) {
193 UpdatePanEvent(gesture, last_gesture, type, time_difference); 173 UpdatePanEvent(gesture, last_gesture, type, time_difference);
194 return; 174 return;
195 } 175 }
196 176
197 // Promote to press type 177 // Promote to press type
198 if (last_entry.type == TouchType::Touch) { 178 if (last_entry.type == GestureType::Touch) {
199 type = TouchType::Press; 179 type = GestureType::Press;
200 } 180 }
201} 181}
202 182
203void Controller_Gesture::EndGesture(GestureProperties& gesture, 183void Controller_Gesture::EndGesture(GestureProperties& gesture,
204 GestureProperties& last_gesture_props, TouchType& type, 184 GestureProperties& last_gesture_props, GestureType& type,
205 Attribute& attributes, f32 time_difference) { 185 GestureAttribute& attributes, f32 time_difference) {
206 const auto& last_entry = GetLastGestureEntry(); 186 const auto& last_entry = GetLastGestureEntry();
207 187
208 if (last_gesture_props.active_points != 0) { 188 if (last_gesture_props.active_points != 0) {
209 switch (last_entry.type) { 189 switch (last_entry.type) {
210 case TouchType::Touch: 190 case GestureType::Touch:
211 if (enable_press_and_tap) { 191 if (enable_press_and_tap) {
212 SetTapEvent(gesture, last_gesture_props, type, attributes); 192 SetTapEvent(gesture, last_gesture_props, type, attributes);
213 return; 193 return;
214 } 194 }
215 type = TouchType::Cancel; 195 type = GestureType::Cancel;
216 force_update = true; 196 force_update = true;
217 break; 197 break;
218 case TouchType::Press: 198 case GestureType::Press:
219 case TouchType::Tap: 199 case GestureType::Tap:
220 case TouchType::Swipe: 200 case GestureType::Swipe:
221 case TouchType::Pinch: 201 case GestureType::Pinch:
222 case TouchType::Rotate: 202 case GestureType::Rotate:
223 type = TouchType::Complete; 203 type = GestureType::Complete;
224 force_update = true; 204 force_update = true;
225 break; 205 break;
226 case TouchType::Pan: 206 case GestureType::Pan:
227 EndPanEvent(gesture, last_gesture_props, type, time_difference); 207 EndPanEvent(gesture, last_gesture_props, type, time_difference);
228 break; 208 break;
229 default: 209 default:
@@ -231,15 +211,15 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
231 } 211 }
232 return; 212 return;
233 } 213 }
234 if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) { 214 if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
235 gesture.detection_count++; 215 gesture.detection_count++;
236 } 216 }
237} 217}
238 218
239void Controller_Gesture::SetTapEvent(GestureProperties& gesture, 219void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
240 GestureProperties& last_gesture_props, TouchType& type, 220 GestureProperties& last_gesture_props, GestureType& type,
241 Attribute& attributes) { 221 GestureAttribute& attributes) {
242 type = TouchType::Tap; 222 type = GestureType::Tap;
243 gesture = last_gesture_props; 223 gesture = last_gesture_props;
244 force_update = true; 224 force_update = true;
245 f32 tap_time_difference = 225 f32 tap_time_difference =
@@ -251,44 +231,42 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
251} 231}
252 232
253void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, 233void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
254 GestureProperties& last_gesture_props, TouchType& type, 234 GestureProperties& last_gesture_props, GestureType& type,
255 f32 time_difference) { 235 f32 time_difference) {
256 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
257 const auto& last_entry = GetLastGestureEntry(); 236 const auto& last_entry = GetLastGestureEntry();
258 237
259 cur_entry.delta = gesture.mid_point - last_entry.pos; 238 next_state.delta = gesture.mid_point - last_entry.pos;
260 cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference; 239 next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
261 cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference; 240 next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
262 last_pan_time_difference = time_difference; 241 last_pan_time_difference = time_difference;
263 242
264 // Promote to pinch type 243 // Promote to pinch type
265 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > 244 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
266 pinch_threshold) { 245 pinch_threshold) {
267 type = TouchType::Pinch; 246 type = GestureType::Pinch;
268 cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance; 247 next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
269 } 248 }
270 249
271 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / 250 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
272 (1 + (gesture.angle * last_gesture_props.angle))); 251 (1 + (gesture.angle * last_gesture_props.angle)));
273 // Promote to rotate type 252 // Promote to rotate type
274 if (std::abs(angle_between_two_lines) > angle_threshold) { 253 if (std::abs(angle_between_two_lines) > angle_threshold) {
275 type = TouchType::Rotate; 254 type = GestureType::Rotate;
276 cur_entry.scale = 0; 255 next_state.scale = 0;
277 cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; 256 next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
278 } 257 }
279} 258}
280 259
281void Controller_Gesture::EndPanEvent(GestureProperties& gesture, 260void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
282 GestureProperties& last_gesture_props, TouchType& type, 261 GestureProperties& last_gesture_props, GestureType& type,
283 f32 time_difference) { 262 f32 time_difference) {
284 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
285 const auto& last_entry = GetLastGestureEntry(); 263 const auto& last_entry = GetLastGestureEntry();
286 cur_entry.vel_x = 264 next_state.vel_x =
287 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); 265 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
288 cur_entry.vel_y = 266 next_state.vel_y =
289 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); 267 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
290 const f32 curr_vel = 268 const f32 curr_vel =
291 std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y)); 269 std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
292 270
293 // Set swipe event with parameters 271 // Set swipe event with parameters
294 if (curr_vel > swipe_threshold) { 272 if (curr_vel > swipe_threshold) {
@@ -297,105 +275,50 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
297 } 275 }
298 276
299 // End panning without swipe 277 // End panning without swipe
300 type = TouchType::Complete; 278 type = GestureType::Complete;
301 cur_entry.vel_x = 0; 279 next_state.vel_x = 0;
302 cur_entry.vel_y = 0; 280 next_state.vel_y = 0;
303 force_update = true; 281 force_update = true;
304} 282}
305 283
306void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, 284void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
307 GestureProperties& last_gesture_props, TouchType& type) { 285 GestureProperties& last_gesture_props, GestureType& type) {
308 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
309 const auto& last_entry = GetLastGestureEntry(); 286 const auto& last_entry = GetLastGestureEntry();
310 287
311 type = TouchType::Swipe; 288 type = GestureType::Swipe;
312 gesture = last_gesture_props; 289 gesture = last_gesture_props;
313 force_update = true; 290 force_update = true;
314 cur_entry.delta = last_entry.delta; 291 next_state.delta = last_entry.delta;
315 292
316 if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) { 293 if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
317 if (cur_entry.delta.x > 0) { 294 if (next_state.delta.x > 0) {
318 cur_entry.direction = Direction::Right; 295 next_state.direction = GestureDirection::Right;
319 return; 296 return;
320 } 297 }
321 cur_entry.direction = Direction::Left; 298 next_state.direction = GestureDirection::Left;
322 return; 299 return;
323 } 300 }
324 if (cur_entry.delta.y > 0) { 301 if (next_state.delta.y > 0) {
325 cur_entry.direction = Direction::Down; 302 next_state.direction = GestureDirection::Down;
326 return; 303 return;
327 } 304 }
328 cur_entry.direction = Direction::Up; 305 next_state.direction = GestureDirection::Up;
329}
330
331void Controller_Gesture::OnLoadInputDevices() {
332 touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
333 touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
334 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
335}
336
337std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
338 // Dont assign any touch input to a point if disabled
339 if (!Settings::values.touchscreen.enabled) {
340 return std::nullopt;
341 }
342 std::size_t first_free_id = 0;
343 while (first_free_id < MAX_POINTS) {
344 if (!fingers[first_free_id].pressed) {
345 return first_free_id;
346 } else {
347 first_free_id++;
348 }
349 }
350 return std::nullopt;
351}
352
353Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() {
354 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
355} 306}
356 307
357const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { 308const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
358 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; 309 return gesture_lifo.ReadCurrentEntry().state;
359}
360
361std::size_t Controller_Gesture::UpdateTouchInputEvent(
362 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
363 const auto& [x, y, pressed] = touch_input;
364 if (finger_id > MAX_POINTS) {
365 LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
366 return MAX_POINTS;
367 }
368 if (pressed) {
369 if (finger_id == MAX_POINTS) {
370 const auto first_free_id = GetUnusedFingerID();
371 if (!first_free_id) {
372 // Invalid finger id do nothing
373 return MAX_POINTS;
374 }
375 finger_id = first_free_id.value();
376 fingers[finger_id].pressed = true;
377 }
378 fingers[finger_id].pos = {x, y};
379 return finger_id;
380 }
381
382 if (finger_id != MAX_POINTS) {
383 fingers[finger_id].pressed = false;
384 }
385
386 return MAX_POINTS;
387} 310}
388 311
389Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { 312Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
390 GestureProperties gesture; 313 GestureProperties gesture;
391 std::array<Finger, MAX_POINTS> active_fingers; 314 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
392 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 315 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
393 [](const auto& finger) { return finger.pressed; }); 316 [](const auto& finger) { return finger.pressed; });
394 gesture.active_points = 317 gesture.active_points =
395 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 318 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
396 319
397 for (size_t id = 0; id < gesture.active_points; ++id) { 320 for (size_t id = 0; id < gesture.active_points; ++id) {
398 const auto& [active_x, active_y] = active_fingers[id].pos; 321 const auto& [active_x, active_y] = active_fingers[id].position;
399 gesture.points[id] = { 322 gesture.points[id] = {
400 .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), 323 .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
401 .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), 324 .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 7e7ae6625..0936a3fa3 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -8,13 +8,14 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h" 10#include "common/point.h"
11#include "core/frontend/input.h" 11#include "core/hid/emulated_console.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
13 14
14namespace Service::HID { 15namespace Service::HID {
15class Controller_Gesture final : public ControllerBase { 16class Controller_Gesture final : public ControllerBase {
16public: 17public:
17 explicit Controller_Gesture(Core::System& system_); 18 explicit Controller_Gesture(Core::HID::HIDCore& hid_core_);
18 ~Controller_Gesture() override; 19 ~Controller_Gesture() override;
19 20
20 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -26,14 +27,12 @@ public:
26 // When the controller is requesting an update for the shared memory 27 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; 28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
28 29
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32private: 30private:
33 static constexpr size_t MAX_FINGERS = 16; 31 static constexpr size_t MAX_FINGERS = 16;
34 static constexpr size_t MAX_POINTS = 4; 32 static constexpr size_t MAX_POINTS = 4;
35 33
36 enum class TouchType : u32 { 34 // This is nn::hid::GestureType
35 enum class GestureType : u32 {
37 Idle, // Nothing touching the screen 36 Idle, // Nothing touching the screen
38 Complete, // Set at the end of a touch event 37 Complete, // Set at the end of a touch event
39 Cancel, // Set when the number of fingers change 38 Cancel, // Set when the number of fingers change
@@ -46,7 +45,8 @@ private:
46 Rotate, // All points rotating from the midpoint 45 Rotate, // All points rotating from the midpoint
47 }; 46 };
48 47
49 enum class Direction : u32 { 48 // This is nn::hid::GestureDirection
49 enum class GestureDirection : u32 {
50 None, 50 None,
51 Left, 51 Left,
52 Up, 52 Up,
@@ -54,51 +54,41 @@ private:
54 Down, 54 Down,
55 }; 55 };
56 56
57 struct Attribute { 57 // This is nn::hid::GestureAttribute
58 struct GestureAttribute {
58 union { 59 union {
59 u32_le raw{}; 60 u32 raw{};
60 61
61 BitField<4, 1, u32> is_new_touch; 62 BitField<4, 1, u32> is_new_touch;
62 BitField<8, 1, u32> is_double_tap; 63 BitField<8, 1, u32> is_double_tap;
63 }; 64 };
64 }; 65 };
65 static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size"); 66 static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
66 67
68 // This is nn::hid::GestureState
67 struct GestureState { 69 struct GestureState {
68 s64_le sampling_number; 70 s64 sampling_number;
69 s64_le sampling_number2; 71 s64 detection_count;
70 s64_le detection_count; 72 GestureType type;
71 TouchType type; 73 GestureDirection direction;
72 Direction direction; 74 Common::Point<s32> pos;
73 Common::Point<s32_le> pos; 75 Common::Point<s32> delta;
74 Common::Point<s32_le> delta;
75 f32 vel_x; 76 f32 vel_x;
76 f32 vel_y; 77 f32 vel_y;
77 Attribute attributes; 78 GestureAttribute attributes;
78 f32 scale; 79 f32 scale;
79 f32 rotation_angle; 80 f32 rotation_angle;
80 s32_le point_count; 81 s32 point_count;
81 std::array<Common::Point<s32_le>, 4> points; 82 std::array<Common::Point<s32>, 4> points;
82 };
83 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
84
85 struct SharedMemory {
86 CommonHeader header;
87 std::array<GestureState, 17> gesture_states;
88 };
89 static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
90
91 struct Finger {
92 Common::Point<f32> pos{};
93 bool pressed{};
94 }; 83 };
84 static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
95 85
96 struct GestureProperties { 86 struct GestureProperties {
97 std::array<Common::Point<s32_le>, MAX_POINTS> points{}; 87 std::array<Common::Point<s32>, MAX_POINTS> points{};
98 std::size_t active_points{}; 88 std::size_t active_points{};
99 Common::Point<s32_le> mid_point{}; 89 Common::Point<s32> mid_point{};
100 s64_le detection_count{}; 90 s64 detection_count{};
101 u64_le delta_time{}; 91 u64 delta_time{};
102 f32 average_distance{}; 92 f32 average_distance{};
103 f32 angle{}; 93 f32 angle{};
104 }; 94 };
@@ -114,61 +104,48 @@ private:
114 f32 time_difference); 104 f32 time_difference);
115 105
116 // Initializes new gesture 106 // Initializes new gesture
117 void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes); 107 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
118 108
119 // Updates existing gesture state 109 // Updates existing gesture state
120 void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference); 110 void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
121 111
122 // Terminates exiting gesture 112 // Terminates exiting gesture
123 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, 113 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
124 TouchType& type, Attribute& attributes, f32 time_difference); 114 GestureType& type, GestureAttribute& attributes, f32 time_difference);
125 115
126 // Set current event to a tap event 116 // Set current event to a tap event
127 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 117 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
128 TouchType& type, Attribute& attributes); 118 GestureType& type, GestureAttribute& attributes);
129 119
130 // Calculates and set the extra parameters related to a pan event 120 // Calculates and set the extra parameters related to a pan event
131 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 121 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
132 TouchType& type, f32 time_difference); 122 GestureType& type, f32 time_difference);
133 123
134 // Terminates the pan event 124 // Terminates the pan event
135 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 125 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
136 TouchType& type, f32 time_difference); 126 GestureType& type, f32 time_difference);
137 127
138 // Set current event to a swipe event 128 // Set current event to a swipe event
139 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 129 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
140 TouchType& type); 130 GestureType& type);
141
142 // Returns an unused finger id, if there is no fingers available std::nullopt is returned.
143 [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const;
144 131
145 // Retrieves the last gesture entry, as indicated by shared memory indices. 132 // Retrieves the last gesture entry, as indicated by shared memory indices.
146 [[nodiscard]] GestureState& GetLastGestureEntry();
147 [[nodiscard]] const GestureState& GetLastGestureEntry() const; 133 [[nodiscard]] const GestureState& GetLastGestureEntry() const;
148 134
149 /**
150 * If the touch is new it tries to assign a new finger id, if there is no fingers available no
151 * changes will be made. Updates the coordinates if the finger id it's already set. If the touch
152 * ends delays the output by one frame to set the end_touch flag before finally freeing the
153 * finger id
154 */
155 size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
156 size_t finger_id);
157
158 // Returns the average distance, angle and middle point of the active fingers 135 // Returns the average distance, angle and middle point of the active fingers
159 GestureProperties GetGestureProperties(); 136 GestureProperties GetGestureProperties();
160 137
161 SharedMemory shared_memory{}; 138 // This is nn::hid::detail::GestureLifo
162 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 139 Lifo<GestureState, hid_entry_count> gesture_lifo{};
163 std::unique_ptr<Input::TouchDevice> touch_udp_device; 140 static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
164 std::unique_ptr<Input::TouchDevice> touch_btn_device; 141 GestureState next_state{};
165 std::array<size_t, MAX_FINGERS> mouse_finger_id{}; 142
166 std::array<size_t, MAX_FINGERS> keyboard_finger_id{}; 143 Core::HID::EmulatedConsole* console;
167 std::array<size_t, MAX_FINGERS> udp_finger_id{}; 144
168 std::array<Finger, MAX_POINTS> fingers{}; 145 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
169 GestureProperties last_gesture{}; 146 GestureProperties last_gesture{};
170 s64_le last_update_timestamp{}; 147 s64 last_update_timestamp{};
171 s64_le last_tap_timestamp{}; 148 s64 last_tap_timestamp{};
172 f32 last_pan_time_difference{}; 149 f32 last_pan_time_difference{};
173 bool force_update{false}; 150 bool force_update{false};
174 bool enable_press_and_tap{false}; 151 bool enable_press_and_tap{false};
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index c6c620008..9588a6910 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -6,13 +6,18 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hid/emulated_devices.h"
10#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/controllers/keyboard.h" 11#include "core/hle/service/hid/controllers/keyboard.h"
10 12
11namespace Service::HID { 13namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8;
14 15
15Controller_Keyboard::Controller_Keyboard(Core::System& system_) : ControllerBase{system_} {} 16Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_)
17 : ControllerBase{hid_core_} {
18 emulated_devices = hid_core.GetEmulatedDevices();
19}
20
16Controller_Keyboard::~Controller_Keyboard() = default; 21Controller_Keyboard::~Controller_Keyboard() = default;
17 22
18void Controller_Keyboard::OnInit() {} 23void Controller_Keyboard::OnInit() {}
@@ -21,51 +26,27 @@ void Controller_Keyboard::OnRelease() {}
21 26
22void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 27void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
23 std::size_t size) { 28 std::size_t size) {
24 shared_memory.header.timestamp = core_timing.GetCPUTicks();
25 shared_memory.header.total_entry_count = 17;
26
27 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
28 shared_memory.header.entry_count = 0; 30 keyboard_lifo.buffer_count = 0;
29 shared_memory.header.last_entry_index = 0; 31 keyboard_lifo.buffer_tail = 0;
32 std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
30 return; 33 return;
31 } 34 }
32 shared_memory.header.entry_count = 16;
33
34 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
35 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
36 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
37 35
38 cur_entry.sampling_number = last_entry.sampling_number + 1; 36 const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state;
39 cur_entry.sampling_number2 = cur_entry.sampling_number; 37 next_state.sampling_number = last_entry.sampling_number + 1;
40 38
41 cur_entry.key.fill(0);
42 if (Settings::values.keyboard_enabled) { 39 if (Settings::values.keyboard_enabled) {
43 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { 40 const auto& keyboard_state = emulated_devices->GetKeyboard();
44 auto& entry = cur_entry.key[i / KEYS_PER_BYTE]; 41 const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
45 entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)));
46 }
47 42
48 using namespace Settings::NativeKeyboard; 43 next_state.key = keyboard_state;
49 44 next_state.modifier = keyboard_modifier_state;
50 // TODO: Assign the correct key to all modifiers 45 next_state.attribute.is_connected.Assign(1);
51 cur_entry.modifier.control.Assign(keyboard_mods[LeftControl]->GetStatus());
52 cur_entry.modifier.shift.Assign(keyboard_mods[LeftShift]->GetStatus());
53 cur_entry.modifier.left_alt.Assign(keyboard_mods[LeftAlt]->GetStatus());
54 cur_entry.modifier.right_alt.Assign(keyboard_mods[RightAlt]->GetStatus());
55 cur_entry.modifier.gui.Assign(0);
56 cur_entry.modifier.caps_lock.Assign(keyboard_mods[CapsLock]->GetStatus());
57 cur_entry.modifier.scroll_lock.Assign(keyboard_mods[ScrollLock]->GetStatus());
58 cur_entry.modifier.num_lock.Assign(keyboard_mods[NumLock]->GetStatus());
59 cur_entry.modifier.katakana.Assign(0);
60 cur_entry.modifier.hiragana.Assign(0);
61 } 46 }
62 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
63}
64 47
65void Controller_Keyboard::OnLoadInputDevices() { 48 keyboard_lifo.WriteNextEntry(next_state);
66 std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(), 49 std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
67 keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
68 std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
69 keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
70} 50}
51
71} // namespace Service::HID 52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 172a80e9c..cf62d3896 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -8,15 +8,20 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
12#include "common/swap.h" 11#include "common/swap.h"
13#include "core/frontend/input.h"
14#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedDevices;
17struct KeyboardModifier;
18struct KeyboardKey;
19} // namespace Core::HID
15 20
16namespace Service::HID { 21namespace Service::HID {
17class Controller_Keyboard final : public ControllerBase { 22class Controller_Keyboard final : public ControllerBase {
18public: 23public:
19 explicit Controller_Keyboard(Core::System& system_); 24 explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_);
20 ~Controller_Keyboard() override; 25 ~Controller_Keyboard() override;
21 26
22 // Called when the controller is initialized 27 // Called when the controller is initialized
@@ -28,47 +33,21 @@ public:
28 // When the controller is requesting an update for the shared memory 33 // When the controller is requesting an update for the shared memory
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 34 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
30 35
31 // Called when input devices should be loaded
32 void OnLoadInputDevices() override;
33
34private: 36private:
35 struct Modifiers { 37 // This is nn::hid::detail::KeyboardState
36 union {
37 u32_le raw{};
38 BitField<0, 1, u32> control;
39 BitField<1, 1, u32> shift;
40 BitField<2, 1, u32> left_alt;
41 BitField<3, 1, u32> right_alt;
42 BitField<4, 1, u32> gui;
43 BitField<8, 1, u32> caps_lock;
44 BitField<9, 1, u32> scroll_lock;
45 BitField<10, 1, u32> num_lock;
46 BitField<11, 1, u32> katakana;
47 BitField<12, 1, u32> hiragana;
48 };
49 };
50 static_assert(sizeof(Modifiers) == 0x4, "Modifiers is an invalid size");
51
52 struct KeyboardState { 38 struct KeyboardState {
53 s64_le sampling_number; 39 s64 sampling_number;
54 s64_le sampling_number2; 40 Core::HID::KeyboardModifier modifier;
55 41 Core::HID::KeyboardAttribute attribute;
56 Modifiers modifier; 42 Core::HID::KeyboardKey key;
57 std::array<u8, 32> key;
58 }; 43 };
59 static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size"); 44 static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
60 45
61 struct SharedMemory { 46 // This is nn::hid::detail::KeyboardLifo
62 CommonHeader header; 47 Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
63 std::array<KeyboardState, 17> pad_states; 48 static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
64 INSERT_PADDING_BYTES(0x28); 49 KeyboardState next_state{};
65 };
66 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
67 SharedMemory shared_memory{};
68 50
69 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys> 51 Core::HID::EmulatedDevices* emulated_devices;
70 keyboard_keys;
71 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
72 keyboard_mods;
73}; 52};
74} // namespace Service::HID 53} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 544a71948..ba79888ae 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -6,12 +6,17 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "core/hid/emulated_devices.h"
10#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/controllers/mouse.h" 11#include "core/hle/service/hid/controllers/mouse.h"
10 12
11namespace Service::HID { 13namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
13 15
14Controller_Mouse::Controller_Mouse(Core::System& system_) : ControllerBase{system_} {} 16Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
17 emulated_devices = hid_core.GetEmulatedDevices();
18}
19
15Controller_Mouse::~Controller_Mouse() = default; 20Controller_Mouse::~Controller_Mouse() = default;
16 21
17void Controller_Mouse::OnInit() {} 22void Controller_Mouse::OnInit() {}
@@ -19,50 +24,35 @@ void Controller_Mouse::OnRelease() {}
19 24
20void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 25void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 26 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetCPUTicks();
23 shared_memory.header.total_entry_count = 17;
24
25 if (!IsControllerActivated()) { 27 if (!IsControllerActivated()) {
26 shared_memory.header.entry_count = 0; 28 mouse_lifo.buffer_count = 0;
27 shared_memory.header.last_entry_index = 0; 29 mouse_lifo.buffer_tail = 0;
30 std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
28 return; 31 return;
29 } 32 }
30 shared_memory.header.entry_count = 16;
31 33
32 auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index]; 34 const auto& last_entry = mouse_lifo.ReadCurrentEntry().state;
33 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 35 next_state.sampling_number = last_entry.sampling_number + 1;
34 auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
35 36
36 cur_entry.sampling_number = last_entry.sampling_number + 1; 37 next_state.attribute.raw = 0;
37 cur_entry.sampling_number2 = cur_entry.sampling_number;
38
39 cur_entry.attribute.raw = 0;
40 if (Settings::values.mouse_enabled) { 38 if (Settings::values.mouse_enabled) {
41 const auto [px, py, sx, sy] = mouse_device->GetStatus(); 39 const auto& mouse_button_state = emulated_devices->GetMouseButtons();
42 const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width); 40 const auto& mouse_position_state = emulated_devices->GetMousePosition();
43 const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height); 41 const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
44 cur_entry.x = x; 42 next_state.attribute.is_connected.Assign(1);
45 cur_entry.y = y; 43 next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
46 cur_entry.delta_x = x - last_entry.x; 44 next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
47 cur_entry.delta_y = y - last_entry.y; 45 next_state.delta_x = next_state.x - last_entry.x;
48 cur_entry.mouse_wheel_x = sx; 46 next_state.delta_y = next_state.y - last_entry.y;
49 cur_entry.mouse_wheel_y = sy; 47 next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
50 cur_entry.attribute.is_connected.Assign(1); 48 next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
51 49
52 using namespace Settings::NativeMouseButton; 50 last_mouse_wheel_state = mouse_wheel_state;
53 cur_entry.button.left.Assign(mouse_button_devices[Left]->GetStatus()); 51 next_state.button = mouse_button_state;
54 cur_entry.button.right.Assign(mouse_button_devices[Right]->GetStatus());
55 cur_entry.button.middle.Assign(mouse_button_devices[Middle]->GetStatus());
56 cur_entry.button.forward.Assign(mouse_button_devices[Forward]->GetStatus());
57 cur_entry.button.back.Assign(mouse_button_devices[Back]->GetStatus());
58 } 52 }
59 53
60 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 54 mouse_lifo.WriteNextEntry(next_state);
55 std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
61} 56}
62 57
63void Controller_Mouse::OnLoadInputDevices() {
64 mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
65 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
66 mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
67}
68} // namespace Service::HID 58} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 3d391a798..7559fc78d 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -7,15 +7,20 @@
7#include <array> 7#include <array>
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/settings.h"
11#include "common/swap.h" 10#include "common/swap.h"
12#include "core/frontend/input.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/hle/service/hid/ring_lifo.h"
13
14namespace Core::HID {
15class EmulatedDevices;
16struct MouseState;
17struct AnalogStickState;
18} // namespace Core::HID
14 19
15namespace Service::HID { 20namespace Service::HID {
16class Controller_Mouse final : public ControllerBase { 21class Controller_Mouse final : public ControllerBase {
17public: 22public:
18 explicit Controller_Mouse(Core::System& system_); 23 explicit Controller_Mouse(Core::HID::HIDCore& hid_core_);
19 ~Controller_Mouse() override; 24 ~Controller_Mouse() override;
20 25
21 // Called when the controller is initialized 26 // Called when the controller is initialized
@@ -27,53 +32,13 @@ public:
27 // When the controller is requesting an update for the shared memory 32 // When the controller is requesting an update for the shared memory
28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 33 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
29 34
30 // Called when input devices should be loaded
31 void OnLoadInputDevices() override;
32
33private: 35private:
34 struct Buttons { 36 // This is nn::hid::detail::MouseLifo
35 union { 37 Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
36 u32_le raw{}; 38 static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
37 BitField<0, 1, u32> left; 39 Core::HID::MouseState next_state{};
38 BitField<1, 1, u32> right;
39 BitField<2, 1, u32> middle;
40 BitField<3, 1, u32> forward;
41 BitField<4, 1, u32> back;
42 };
43 };
44 static_assert(sizeof(Buttons) == 0x4, "Buttons is an invalid size");
45
46 struct Attributes {
47 union {
48 u32_le raw{};
49 BitField<0, 1, u32> transferable;
50 BitField<1, 1, u32> is_connected;
51 };
52 };
53 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
54
55 struct MouseState {
56 s64_le sampling_number;
57 s64_le sampling_number2;
58 s32_le x;
59 s32_le y;
60 s32_le delta_x;
61 s32_le delta_y;
62 s32_le mouse_wheel_x;
63 s32_le mouse_wheel_y;
64 Buttons button;
65 Attributes attribute;
66 };
67 static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
68
69 struct SharedMemory {
70 CommonHeader header;
71 std::array<MouseState, 17> mouse_states;
72 };
73 SharedMemory shared_memory{};
74 40
75 std::unique_ptr<Input::MouseDevice> mouse_device; 41 Core::HID::AnalogStickState last_mouse_wheel_state;
76 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> 42 Core::HID::EmulatedDevices* emulated_devices;
77 mouse_button_devices;
78}; 43};
79} // namespace Service::HID 44} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 196876810..2705e9dcb 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -10,9 +10,9 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#include "core/core.h"
14#include "core/core_timing.h" 13#include "core/core_timing.h"
15#include "core/frontend/input.h" 14#include "core/hid/emulated_controller.h"
15#include "core/hid/hid_core.h"
16#include "core/hle/kernel/k_event.h" 16#include "core/hle/kernel/k_event.h"
17#include "core/hle/kernel/k_readable_event.h" 17#include "core/hle/kernel/k_readable_event.h"
18#include "core/hle/kernel/k_writable_event.h" 18#include "core/hle/kernel/k_writable_event.h"
@@ -20,120 +20,26 @@
20#include "core/hle/service/kernel_helpers.h" 20#include "core/hle/service/kernel_helpers.h"
21 21
22namespace Service::HID { 22namespace Service::HID {
23constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
24constexpr s32 HID_TRIGGER_MAX = 0x7fff;
25[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
26constexpr std::size_t NPAD_OFFSET = 0x9A00; 23constexpr std::size_t NPAD_OFFSET = 0x9A00;
27constexpr u32 BATTERY_FULL = 2; 24constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
28constexpr u32 MAX_NPAD_ID = 7; 25 Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
29constexpr std::size_t HANDHELD_INDEX = 8; 26 Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
30constexpr std::array<u32, 10> npad_id_list{ 27 Core::HID::NpadIdType::Player7, Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other,
31 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, 28 Core::HID::NpadIdType::Handheld,
32}; 29};
33 30
34enum class JoystickId : std::size_t { 31bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
35 Joystick_Left,
36 Joystick_Right,
37};
38
39Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
40 Settings::ControllerType type) {
41 switch (type) {
42 case Settings::ControllerType::ProController:
43 return NPadControllerType::ProController;
44 case Settings::ControllerType::DualJoyconDetached:
45 return NPadControllerType::JoyDual;
46 case Settings::ControllerType::LeftJoycon:
47 return NPadControllerType::JoyLeft;
48 case Settings::ControllerType::RightJoycon:
49 return NPadControllerType::JoyRight;
50 case Settings::ControllerType::Handheld:
51 return NPadControllerType::Handheld;
52 case Settings::ControllerType::GameCube:
53 return NPadControllerType::GameCube;
54 default:
55 UNREACHABLE();
56 return NPadControllerType::ProController;
57 }
58}
59
60Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
61 Controller_NPad::NPadControllerType type) {
62 switch (type) {
63 case NPadControllerType::ProController:
64 return Settings::ControllerType::ProController;
65 case NPadControllerType::JoyDual:
66 return Settings::ControllerType::DualJoyconDetached;
67 case NPadControllerType::JoyLeft:
68 return Settings::ControllerType::LeftJoycon;
69 case NPadControllerType::JoyRight:
70 return Settings::ControllerType::RightJoycon;
71 case NPadControllerType::Handheld:
72 return Settings::ControllerType::Handheld;
73 case NPadControllerType::GameCube:
74 return Settings::ControllerType::GameCube;
75 default:
76 UNREACHABLE();
77 return Settings::ControllerType::ProController;
78 }
79}
80
81std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
82 switch (npad_id) { 32 switch (npad_id) {
83 case 0: 33 case Core::HID::NpadIdType::Player1:
84 case 1: 34 case Core::HID::NpadIdType::Player2:
85 case 2: 35 case Core::HID::NpadIdType::Player3:
86 case 3: 36 case Core::HID::NpadIdType::Player4:
87 case 4: 37 case Core::HID::NpadIdType::Player5:
88 case 5: 38 case Core::HID::NpadIdType::Player6:
89 case 6: 39 case Core::HID::NpadIdType::Player7:
90 case 7: 40 case Core::HID::NpadIdType::Player8:
91 return npad_id; 41 case Core::HID::NpadIdType::Other:
92 case HANDHELD_INDEX: 42 case Core::HID::NpadIdType::Handheld:
93 case NPAD_HANDHELD:
94 return HANDHELD_INDEX;
95 case 9:
96 case NPAD_UNKNOWN:
97 return 9;
98 default:
99 UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
100 return 0;
101 }
102}
103
104u32 Controller_NPad::IndexToNPad(std::size_t index) {
105 switch (index) {
106 case 0:
107 case 1:
108 case 2:
109 case 3:
110 case 4:
111 case 5:
112 case 6:
113 case 7:
114 return static_cast<u32>(index);
115 case HANDHELD_INDEX:
116 return NPAD_HANDHELD;
117 case 9:
118 return NPAD_UNKNOWN;
119 default:
120 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
121 return 0;
122 }
123}
124
125bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
126 switch (npad_id) {
127 case 0:
128 case 1:
129 case 2:
130 case 3:
131 case 4:
132 case 5:
133 case 6:
134 case 7:
135 case NPAD_UNKNOWN:
136 case NPAD_HANDHELD:
137 return true; 43 return true;
138 default: 44 default:
139 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id); 45 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
@@ -141,305 +47,337 @@ bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
141 } 47 }
142} 48}
143 49
144bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) { 50bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
145 return IsNpadIdValid(device_handle.npad_id) && 51 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
146 device_handle.npad_type < NpadType::MaxNpadType && 52 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
147 device_handle.device_index < DeviceIndex::MaxDeviceIndex; 53 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
148} 54}
149 55
150Controller_NPad::Controller_NPad(Core::System& system_, 56bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
57 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
58 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
59 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
60}
61
62Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
151 KernelHelpers::ServiceContext& service_context_) 63 KernelHelpers::ServiceContext& service_context_)
152 : ControllerBase{system_}, service_context{service_context_} { 64 : ControllerBase{hid_core_}, service_context{service_context_} {
153 latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}); 65 for (std::size_t i = 0; i < controller_data.size(); ++i) {
66 auto& controller = controller_data[i];
67 controller.device = hid_core.GetEmulatedControllerByIndex(i);
68 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
69 DEFAULT_VIBRATION_VALUE;
70 controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
71 DEFAULT_VIBRATION_VALUE;
72 Core::HID::ControllerUpdateCallback engine_callback{
73 .on_change = [this,
74 i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
75 .is_npad_service = true,
76 };
77 controller.callback_key = controller.device->SetCallback(engine_callback);
78 }
154} 79}
155 80
156Controller_NPad::~Controller_NPad() { 81Controller_NPad::~Controller_NPad() {
82 for (std::size_t i = 0; i < controller_data.size(); ++i) {
83 auto& controller = controller_data[i];
84 controller.device->DeleteCallback(controller.callback_key);
85 }
157 OnRelease(); 86 OnRelease();
158} 87}
159 88
160void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { 89void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
161 const auto controller_type = connected_controllers[controller_idx].type; 90 std::size_t controller_idx) {
162 auto& controller = shared_memory_entries[controller_idx]; 91 if (type == Core::HID::ControllerTriggerType::All) {
163 if (controller_type == NPadControllerType::None) { 92 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
164 styleset_changed_events[controller_idx]->GetWritableEvent().Signal(); 93 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
94 return;
95 }
96 if (controller_idx >= controller_data.size()) {
97 return;
98 }
99
100 auto& controller = controller_data[controller_idx];
101 const auto is_connected = controller.device->IsConnected();
102 const auto npad_type = controller.device->GetNpadStyleIndex();
103 const auto npad_id = controller.device->GetNpadIdType();
104 switch (type) {
105 case Core::HID::ControllerTriggerType::Connected:
106 case Core::HID::ControllerTriggerType::Disconnected:
107 if (is_connected == controller.is_connected) {
108 return;
109 }
110 UpdateControllerAt(npad_type, npad_id, is_connected);
111 break;
112 case Core::HID::ControllerTriggerType::Battery: {
113 if (!controller.device->IsConnected()) {
114 return;
115 }
116 auto& shared_memory = controller.shared_memory_entry;
117 const auto& battery_level = controller.device->GetBattery();
118 shared_memory.battery_level_dual = battery_level.dual.battery_level;
119 shared_memory.battery_level_left = battery_level.left.battery_level;
120 shared_memory.battery_level_right = battery_level.right.battery_level;
121 break;
122 }
123 default:
124 break;
125 }
126}
127
128void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
129 auto& controller = GetControllerFromNpadIdType(npad_id);
130 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
165 return; 131 return;
166 } 132 }
167 controller.style_set.raw = 0; // Zero out 133 LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
168 controller.device_type.raw = 0; 134 const auto controller_type = controller.device->GetNpadStyleIndex();
169 controller.system_properties.raw = 0; 135 auto& shared_memory = controller.shared_memory_entry;
136 if (controller_type == Core::HID::NpadStyleIndex::None) {
137 controller.styleset_changed_event->GetWritableEvent().Signal();
138 return;
139 }
140 shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None;
141 shared_memory.device_type.raw = 0;
142 shared_memory.system_properties.raw = 0;
170 switch (controller_type) { 143 switch (controller_type) {
171 case NPadControllerType::None: 144 case Core::HID::NpadStyleIndex::None:
172 UNREACHABLE(); 145 UNREACHABLE();
173 break; 146 break;
174 case NPadControllerType::ProController: 147 case Core::HID::NpadStyleIndex::ProController:
175 controller.style_set.fullkey.Assign(1); 148 shared_memory.style_tag.fullkey.Assign(1);
176 controller.device_type.fullkey.Assign(1); 149 shared_memory.device_type.fullkey.Assign(1);
177 controller.system_properties.is_vertical.Assign(1); 150 shared_memory.system_properties.is_vertical.Assign(1);
178 controller.system_properties.use_plus.Assign(1); 151 shared_memory.system_properties.use_plus.Assign(1);
179 controller.system_properties.use_minus.Assign(1); 152 shared_memory.system_properties.use_minus.Assign(1);
180 controller.assignment_mode = NpadAssignments::Single; 153 shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
181 controller.footer_type = AppletFooterUiType::SwitchProController;
182 break; 154 break;
183 case NPadControllerType::Handheld: 155 case Core::HID::NpadStyleIndex::Handheld:
184 controller.style_set.handheld.Assign(1); 156 shared_memory.style_tag.handheld.Assign(1);
185 controller.device_type.handheld_left.Assign(1); 157 shared_memory.device_type.handheld_left.Assign(1);
186 controller.device_type.handheld_right.Assign(1); 158 shared_memory.device_type.handheld_right.Assign(1);
187 controller.system_properties.is_vertical.Assign(1); 159 shared_memory.system_properties.is_vertical.Assign(1);
188 controller.system_properties.use_plus.Assign(1); 160 shared_memory.system_properties.use_plus.Assign(1);
189 controller.system_properties.use_minus.Assign(1); 161 shared_memory.system_properties.use_minus.Assign(1);
190 controller.assignment_mode = NpadAssignments::Dual; 162 shared_memory.system_properties.use_directional_buttons.Assign(1);
191 controller.footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; 163 shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
164 shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
165 break;
166 case Core::HID::NpadStyleIndex::JoyconDual:
167 shared_memory.style_tag.joycon_dual.Assign(1);
168 if (controller.is_dual_left_connected) {
169 shared_memory.device_type.joycon_left.Assign(1);
170 shared_memory.system_properties.use_minus.Assign(1);
171 }
172 if (controller.is_dual_right_connected) {
173 shared_memory.device_type.joycon_right.Assign(1);
174 shared_memory.system_properties.use_plus.Assign(1);
175 }
176 shared_memory.system_properties.use_directional_buttons.Assign(1);
177 shared_memory.system_properties.is_vertical.Assign(1);
178 shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
179 if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
180 shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
181 } else if (controller.is_dual_left_connected) {
182 shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
183 } else {
184 shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
185 }
192 break; 186 break;
193 case NPadControllerType::JoyDual: 187 case Core::HID::NpadStyleIndex::JoyconLeft:
194 controller.style_set.joycon_dual.Assign(1); 188 shared_memory.style_tag.joycon_left.Assign(1);
195 controller.device_type.joycon_left.Assign(1); 189 shared_memory.device_type.joycon_left.Assign(1);
196 controller.device_type.joycon_right.Assign(1); 190 shared_memory.system_properties.is_horizontal.Assign(1);
197 controller.system_properties.is_vertical.Assign(1); 191 shared_memory.system_properties.use_minus.Assign(1);
198 controller.system_properties.use_plus.Assign(1); 192 shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
199 controller.system_properties.use_minus.Assign(1);
200 controller.assignment_mode = NpadAssignments::Dual;
201 controller.footer_type = AppletFooterUiType::JoyDual;
202 break; 193 break;
203 case NPadControllerType::JoyLeft: 194 case Core::HID::NpadStyleIndex::JoyconRight:
204 controller.style_set.joycon_left.Assign(1); 195 shared_memory.style_tag.joycon_right.Assign(1);
205 controller.device_type.joycon_left.Assign(1); 196 shared_memory.device_type.joycon_right.Assign(1);
206 controller.system_properties.is_horizontal.Assign(1); 197 shared_memory.system_properties.is_horizontal.Assign(1);
207 controller.system_properties.use_minus.Assign(1); 198 shared_memory.system_properties.use_plus.Assign(1);
208 controller.assignment_mode = NpadAssignments::Single; 199 shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
209 controller.footer_type = AppletFooterUiType::JoyLeftHorizontal;
210 break; 200 break;
211 case NPadControllerType::JoyRight: 201 case Core::HID::NpadStyleIndex::GameCube:
212 controller.style_set.joycon_right.Assign(1); 202 shared_memory.style_tag.gamecube.Assign(1);
213 controller.device_type.joycon_right.Assign(1); 203 shared_memory.device_type.fullkey.Assign(1);
214 controller.system_properties.is_horizontal.Assign(1); 204 shared_memory.system_properties.is_vertical.Assign(1);
215 controller.system_properties.use_plus.Assign(1); 205 shared_memory.system_properties.use_plus.Assign(1);
216 controller.assignment_mode = NpadAssignments::Single;
217 controller.footer_type = AppletFooterUiType::JoyRightHorizontal;
218 break; 206 break;
219 case NPadControllerType::GameCube: 207 case Core::HID::NpadStyleIndex::Pokeball:
220 controller.style_set.gamecube.Assign(1); 208 shared_memory.style_tag.palma.Assign(1);
221 // The GC Controller behaves like a wired Pro Controller 209 shared_memory.device_type.palma.Assign(1);
222 controller.device_type.fullkey.Assign(1);
223 controller.system_properties.is_vertical.Assign(1);
224 controller.system_properties.use_plus.Assign(1);
225 break; 210 break;
226 case NPadControllerType::Pokeball: 211 case Core::HID::NpadStyleIndex::NES:
227 controller.style_set.palma.Assign(1); 212 shared_memory.style_tag.lark.Assign(1);
228 controller.device_type.palma.Assign(1); 213 shared_memory.device_type.fullkey.Assign(1);
229 controller.assignment_mode = NpadAssignments::Single; 214 break;
215 case Core::HID::NpadStyleIndex::SNES:
216 shared_memory.style_tag.lucia.Assign(1);
217 shared_memory.device_type.fullkey.Assign(1);
218 shared_memory.applet_footer.type = AppletFooterUiType::Lucia;
219 break;
220 case Core::HID::NpadStyleIndex::N64:
221 shared_memory.style_tag.lagoon.Assign(1);
222 shared_memory.device_type.fullkey.Assign(1);
223 shared_memory.applet_footer.type = AppletFooterUiType::Lagon;
224 break;
225 case Core::HID::NpadStyleIndex::SegaGenesis:
226 shared_memory.style_tag.lager.Assign(1);
227 shared_memory.device_type.fullkey.Assign(1);
228 break;
229 default:
230 break; 230 break;
231 } 231 }
232 232
233 controller.fullkey_color.attribute = ColorAttributes::Ok; 233 const auto& body_colors = controller.device->GetColors();
234 controller.fullkey_color.fullkey.body = 0;
235 controller.fullkey_color.fullkey.button = 0;
236 234
237 controller.joycon_color.attribute = ColorAttributes::Ok; 235 shared_memory.fullkey_color.attribute = ColorAttribute::Ok;
238 controller.joycon_color.left.body = 236 shared_memory.fullkey_color.fullkey = body_colors.fullkey;
239 Settings::values.players.GetValue()[controller_idx].body_color_left;
240 controller.joycon_color.left.button =
241 Settings::values.players.GetValue()[controller_idx].button_color_left;
242 controller.joycon_color.right.body =
243 Settings::values.players.GetValue()[controller_idx].body_color_right;
244 controller.joycon_color.right.button =
245 Settings::values.players.GetValue()[controller_idx].button_color_right;
246 237
247 // TODO: Investigate when we should report all batery types 238 shared_memory.joycon_color.attribute = ColorAttribute::Ok;
248 controller.battery_level_dual = BATTERY_FULL; 239 shared_memory.joycon_color.left = body_colors.left;
249 controller.battery_level_left = BATTERY_FULL; 240 shared_memory.joycon_color.right = body_colors.right;
250 controller.battery_level_right = BATTERY_FULL;
251 241
252 SignalStyleSetChangedEvent(IndexToNPad(controller_idx)); 242 // TODO: Investigate when we should report all batery types
243 const auto& battery_level = controller.device->GetBattery();
244 shared_memory.battery_level_dual = battery_level.dual.battery_level;
245 shared_memory.battery_level_left = battery_level.left.battery_level;
246 shared_memory.battery_level_right = battery_level.right.battery_level;
247
248 controller.is_connected = true;
249 controller.device->Connect();
250 SignalStyleSetChangedEvent(npad_id);
251 WriteEmptyEntry(controller.shared_memory_entry);
253} 252}
254 253
255void Controller_NPad::OnInit() { 254void Controller_NPad::OnInit() {
256 for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
257 styleset_changed_events[i] =
258 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
259 }
260
261 if (!IsControllerActivated()) { 255 if (!IsControllerActivated()) {
262 return; 256 return;
263 } 257 }
264 258
265 OnLoadInputDevices(); 259 for (std::size_t i = 0; i < controller_data.size(); ++i) {
266 260 auto& controller = controller_data[i];
267 if (style.raw == 0) { 261 controller.styleset_changed_event =
268 // We want to support all controllers 262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
269 style.handheld.Assign(1);
270 style.joycon_left.Assign(1);
271 style.joycon_right.Assign(1);
272 style.joycon_dual.Assign(1);
273 style.fullkey.Assign(1);
274 style.gamecube.Assign(1);
275 style.palma.Assign(1);
276 }
277
278 std::transform(Settings::values.players.GetValue().begin(),
279 Settings::values.players.GetValue().end(), connected_controllers.begin(),
280 [](const Settings::PlayerInput& player) {
281 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
282 player.connected};
283 });
284
285 // Connect the Player 1 or Handheld controller if none are connected.
286 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
287 [](const ControllerHolder& controller) { return controller.is_connected; })) {
288 const auto controller =
289 MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
290 if (controller == NPadControllerType::Handheld) {
291 Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
292 connected_controllers[HANDHELD_INDEX] = {controller, true};
293 } else {
294 Settings::values.players.GetValue()[0].connected = true;
295 connected_controllers[0] = {controller, true};
296 }
297 } 263 }
298 264
299 // Account for handheld 265 if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
300 if (connected_controllers[HANDHELD_INDEX].is_connected) { 266 // We want to support all controllers
301 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; 267 hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
302 } 268 }
303 269
304 supported_npad_id_types.resize(npad_id_list.size()); 270 supported_npad_id_types.resize(npad_id_list.size());
305 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 271 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
306 npad_id_list.size() * sizeof(u32)); 272 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
273
274 // Prefill controller buffers
275 for (auto& controller : controller_data) {
276 auto& npad = controller.shared_memory_entry;
277 npad.fullkey_color = {
278 .attribute = ColorAttribute::NoController,
279 .fullkey = {},
280 };
281 npad.joycon_color = {
282 .attribute = ColorAttribute::NoController,
283 .left = {},
284 .right = {},
285 };
286 // HW seems to initialize the first 19 entries
287 for (std::size_t i = 0; i < 19; ++i) {
288 WriteEmptyEntry(npad);
289 }
290 }
307 291
308 for (std::size_t i = 0; i < connected_controllers.size(); ++i) { 292 // Connect controllers
309 const auto& controller = connected_controllers[i]; 293 for (auto& controller : controller_data) {
310 if (controller.is_connected) { 294 const auto& device = controller.device;
311 AddNewControllerAt(controller.type, i); 295 if (device->IsConnected()) {
296 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
312 } 297 }
313 } 298 }
314} 299}
315 300
316void Controller_NPad::OnLoadInputDevices() { 301void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
317 const auto& players = Settings::values.players.GetValue(); 302 NPadGenericState dummy_pad_state{};
303 NpadGcTriggerState dummy_gc_state{};
304 dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
305 npad.fullkey_lifo.WriteNextEntry(dummy_pad_state);
306 dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1;
307 npad.handheld_lifo.WriteNextEntry(dummy_pad_state);
308 dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
309 npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state);
310 dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
311 npad.joy_left_lifo.WriteNextEntry(dummy_pad_state);
312 dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
313 npad.joy_right_lifo.WriteNextEntry(dummy_pad_state);
314 dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1;
315 npad.palma_lifo.WriteNextEntry(dummy_pad_state);
316 dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
317 npad.system_ext_lifo.WriteNextEntry(dummy_pad_state);
318 dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
319 npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
320}
318 321
319 std::lock_guard lock{mutex}; 322void Controller_NPad::OnRelease() {
320 for (std::size_t i = 0; i < players.size(); ++i) { 323 for (std::size_t i = 0; i < controller_data.size(); ++i) {
321 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 324 auto& controller = controller_data[i];
322 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 325 service_context.CloseEvent(controller.styleset_changed_event);
323 buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>); 326 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
324 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 327 VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {});
325 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
326 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
327 std::transform(players[i].vibrations.begin() +
328 Settings::NativeVibration::VIBRATION_HID_BEGIN,
329 players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
330 vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
331 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
332 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
333 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
334 for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
335 InitializeVibrationDeviceAtIndex(i, device_idx);
336 } 328 }
337 } 329 }
338} 330}
339 331
340void Controller_NPad::OnRelease() { 332void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
341 for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) { 333 std::lock_guard lock{mutex};
342 for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) { 334 auto& controller = GetControllerFromNpadIdType(npad_id);
343 VibrateControllerAtIndex(npad_idx, device_idx, {}); 335 const auto controller_type = controller.device->GetNpadStyleIndex();
344 } 336 if (!controller.device->IsConnected()) {
337 return;
345 } 338 }
346 339
347 for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { 340 auto& pad_entry = controller.npad_pad_state;
348 service_context.CloseEvent(styleset_changed_events[i]); 341 auto& trigger_entry = controller.npad_trigger_state;
342 const auto button_state = controller.device->GetNpadButtons();
343 const auto stick_state = controller.device->GetSticks();
344
345 using btn = Core::HID::NpadButton;
346 pad_entry.npad_buttons.raw = btn::None;
347 if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) {
348 constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R |
349 btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp |
350 btn::StickRRight | btn::StickRDown;
351 pad_entry.npad_buttons.raw = button_state.raw & right_button_mask;
352 pad_entry.r_stick = stick_state.right;
349 } 353 }
350}
351 354
352void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { 355 if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) {
353 std::lock_guard lock{mutex}; 356 constexpr btn left_button_mask =
357 btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL |
358 btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown;
359 pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask;
360 pad_entry.l_stick = stick_state.left;
361 }
354 362
355 const auto controller_idx = NPadIdToIndex(npad_id); 363 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) {
356 const auto controller_type = connected_controllers[controller_idx].type; 364 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
357 if (!connected_controllers[controller_idx].is_connected) { 365 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
358 return;
359 } 366 }
360 auto& pad_state = npad_pad_states[controller_idx].pad_states; 367
361 auto& lstick_entry = npad_pad_states[controller_idx].l_stick; 368 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) {
362 auto& rstick_entry = npad_pad_states[controller_idx].r_stick; 369 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
363 auto& trigger_entry = npad_trigger_states[controller_idx]; 370 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
364 const auto& button_state = buttons[controller_idx]; 371 }
365 const auto& analog_state = sticks[controller_idx]; 372
366 const auto [stick_l_x_f, stick_l_y_f] = 373 if (controller_type == Core::HID::NpadStyleIndex::GameCube) {
367 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 374 const auto& trigger_state = controller.device->GetTriggers();
368 const auto [stick_r_x_f, stick_r_y_f] = 375 trigger_entry.l_analog = trigger_state.left;
369 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); 376 trigger_entry.r_analog = trigger_state.right;
370 377 pad_entry.npad_buttons.zl.Assign(false);
371 using namespace Settings::NativeButton; 378 pad_entry.npad_buttons.zr.Assign(button_state.r);
372 if (controller_type != NPadControllerType::JoyLeft) { 379 pad_entry.npad_buttons.l.Assign(button_state.zl);
373 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); 380 pad_entry.npad_buttons.r.Assign(button_state.zr);
374 pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
375 pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
376 pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
377 pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
378 pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
379 pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
380 pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
381
382 pad_state.r_stick_right.Assign(
383 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
384 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
385 pad_state.r_stick_left.Assign(
386 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
387 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
388 pad_state.r_stick_up.Assign(
389 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
390 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
391 pad_state.r_stick_down.Assign(
392 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
393 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
394 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
395 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
396 }
397
398 if (controller_type != NPadControllerType::JoyRight) {
399 pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
400 pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
401 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
402 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
403 pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
404 pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
405 pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
406 pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
407
408 pad_state.l_stick_right.Assign(
409 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
410 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
411 pad_state.l_stick_left.Assign(
412 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
413 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
414 pad_state.l_stick_up.Assign(
415 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
416 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
417 pad_state.l_stick_down.Assign(
418 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
419 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
420 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
421 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
422 }
423
424 if (controller_type == NPadControllerType::JoyLeft) {
425 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
426 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
427 }
428
429 if (controller_type == NPadControllerType::JoyRight) {
430 pad_state.right_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
431 pad_state.right_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
432 }
433
434 if (controller_type == NPadControllerType::GameCube) {
435 trigger_entry.l_analog = static_cast<s32>(
436 button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
437 trigger_entry.r_analog = static_cast<s32>(
438 button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
439 pad_state.zl.Assign(false);
440 pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
441 pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
442 pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
443 } 381 }
444} 382}
445 383
@@ -448,173 +386,136 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
448 if (!IsControllerActivated()) { 386 if (!IsControllerActivated()) {
449 return; 387 return;
450 } 388 }
451 for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
452 auto& npad = shared_memory_entries[i];
453 const std::array<NPadGeneric*, 7> controller_npads{
454 &npad.fullkey_states, &npad.handheld_states, &npad.joy_dual_states,
455 &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states,
456 &npad.system_ext_states};
457
458 // There is the posibility to have more controllers with analog triggers
459 const std::array<TriggerGeneric*, 1> controller_triggers{
460 &npad.gc_trigger_states,
461 };
462
463 for (auto* main_controller : controller_npads) {
464 main_controller->common.entry_count = 16;
465 main_controller->common.total_entry_count = 17;
466
467 const auto& last_entry =
468 main_controller->npad[main_controller->common.last_entry_index];
469
470 main_controller->common.timestamp = core_timing.GetCPUTicks();
471 main_controller->common.last_entry_index =
472 (main_controller->common.last_entry_index + 1) % 17;
473
474 auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
475
476 cur_entry.timestamp = last_entry.timestamp + 1;
477 cur_entry.timestamp2 = cur_entry.timestamp;
478 }
479
480 for (auto* analog_trigger : controller_triggers) {
481 analog_trigger->entry_count = 16;
482 analog_trigger->total_entry_count = 17;
483
484 const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
485
486 analog_trigger->timestamp = core_timing.GetCPUTicks();
487 analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
488
489 auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
490 389
491 cur_entry.timestamp = last_entry.timestamp + 1; 390 for (std::size_t i = 0; i < controller_data.size(); ++i) {
492 cur_entry.timestamp2 = cur_entry.timestamp; 391 auto& controller = controller_data[i];
493 } 392 auto& npad = controller.shared_memory_entry;
494 393
495 const auto& controller_type = connected_controllers[i].type; 394 const auto& controller_type = controller.device->GetNpadStyleIndex();
496 395
497 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { 396 if (controller_type == Core::HID::NpadStyleIndex::None ||
397 !controller.device->IsConnected()) {
398 // Refresh shared memory
399 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
400 &controller.shared_memory_entry, sizeof(NpadInternalState));
498 continue; 401 continue;
499 } 402 }
500 const u32 npad_index = static_cast<u32>(i);
501
502 RequestPadStateUpdate(npad_index);
503 auto& pad_state = npad_pad_states[npad_index];
504 auto& trigger_state = npad_trigger_states[npad_index];
505
506 auto& main_controller =
507 npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index];
508 auto& handheld_entry =
509 npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
510 auto& dual_entry = npad.joy_dual_states.npad[npad.joy_dual_states.common.last_entry_index];
511 auto& left_entry = npad.joy_left_states.npad[npad.joy_left_states.common.last_entry_index];
512 auto& right_entry =
513 npad.joy_right_states.npad[npad.joy_right_states.common.last_entry_index];
514 auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];
515 auto& libnx_entry =
516 npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index];
517 auto& trigger_entry =
518 npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];
519
520 libnx_entry.connection_status.raw = 0;
521 libnx_entry.connection_status.is_connected.Assign(1);
522 403
404 RequestPadStateUpdate(controller.device->GetNpadIdType());
405 auto& pad_state = controller.npad_pad_state;
406 auto& libnx_state = controller.npad_libnx_state;
407 auto& trigger_state = controller.npad_trigger_state;
408
409 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
410 // any controllers.
411 libnx_state.connection_status.raw = 0;
412 libnx_state.connection_status.is_connected.Assign(1);
523 switch (controller_type) { 413 switch (controller_type) {
524 case NPadControllerType::None: 414 case Core::HID::NpadStyleIndex::None:
525 UNREACHABLE(); 415 UNREACHABLE();
526 break; 416 break;
527 case NPadControllerType::ProController: 417 case Core::HID::NpadStyleIndex::ProController:
528 main_controller.connection_status.raw = 0; 418 case Core::HID::NpadStyleIndex::NES:
529 main_controller.connection_status.is_connected.Assign(1); 419 case Core::HID::NpadStyleIndex::SNES:
530 main_controller.connection_status.is_wired.Assign(1); 420 case Core::HID::NpadStyleIndex::N64:
531 main_controller.pad.pad_states.raw = pad_state.pad_states.raw; 421 case Core::HID::NpadStyleIndex::SegaGenesis:
532 main_controller.pad.l_stick = pad_state.l_stick; 422 pad_state.connection_status.raw = 0;
533 main_controller.pad.r_stick = pad_state.r_stick; 423 pad_state.connection_status.is_connected.Assign(1);
534 424 pad_state.connection_status.is_wired.Assign(1);
535 libnx_entry.connection_status.is_wired.Assign(1); 425
426 libnx_state.connection_status.is_wired.Assign(1);
427 pad_state.sampling_number =
428 npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
429 npad.fullkey_lifo.WriteNextEntry(pad_state);
536 break; 430 break;
537 case NPadControllerType::Handheld: 431 case Core::HID::NpadStyleIndex::Handheld:
538 handheld_entry.connection_status.raw = 0; 432 pad_state.connection_status.raw = 0;
539 handheld_entry.connection_status.is_connected.Assign(1); 433 pad_state.connection_status.is_connected.Assign(1);
540 handheld_entry.connection_status.is_wired.Assign(1); 434 pad_state.connection_status.is_wired.Assign(1);
541 handheld_entry.connection_status.is_left_connected.Assign(1); 435 pad_state.connection_status.is_left_connected.Assign(1);
542 handheld_entry.connection_status.is_right_connected.Assign(1); 436 pad_state.connection_status.is_right_connected.Assign(1);
543 handheld_entry.connection_status.is_left_wired.Assign(1); 437 pad_state.connection_status.is_left_wired.Assign(1);
544 handheld_entry.connection_status.is_right_wired.Assign(1); 438 pad_state.connection_status.is_right_wired.Assign(1);
545 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; 439
546 handheld_entry.pad.l_stick = pad_state.l_stick; 440 libnx_state.connection_status.is_wired.Assign(1);
547 handheld_entry.pad.r_stick = pad_state.r_stick; 441 libnx_state.connection_status.is_left_connected.Assign(1);
548 442 libnx_state.connection_status.is_right_connected.Assign(1);
549 libnx_entry.connection_status.is_wired.Assign(1); 443 libnx_state.connection_status.is_left_wired.Assign(1);
550 libnx_entry.connection_status.is_left_connected.Assign(1); 444 libnx_state.connection_status.is_right_wired.Assign(1);
551 libnx_entry.connection_status.is_right_connected.Assign(1); 445 pad_state.sampling_number =
552 libnx_entry.connection_status.is_left_wired.Assign(1); 446 npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
553 libnx_entry.connection_status.is_right_wired.Assign(1); 447 npad.handheld_lifo.WriteNextEntry(pad_state);
554 break; 448 break;
555 case NPadControllerType::JoyDual: 449 case Core::HID::NpadStyleIndex::JoyconDual:
556 dual_entry.connection_status.raw = 0; 450 pad_state.connection_status.raw = 0;
557 dual_entry.connection_status.is_connected.Assign(1); 451 pad_state.connection_status.is_connected.Assign(1);
558 dual_entry.connection_status.is_left_connected.Assign(1); 452 if (controller.is_dual_left_connected) {
559 dual_entry.connection_status.is_right_connected.Assign(1); 453 pad_state.connection_status.is_left_connected.Assign(1);
560 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; 454 libnx_state.connection_status.is_left_connected.Assign(1);
561 dual_entry.pad.l_stick = pad_state.l_stick; 455 }
562 dual_entry.pad.r_stick = pad_state.r_stick; 456 if (controller.is_dual_right_connected) {
563 457 pad_state.connection_status.is_right_connected.Assign(1);
564 libnx_entry.connection_status.is_left_connected.Assign(1); 458 libnx_state.connection_status.is_right_connected.Assign(1);
565 libnx_entry.connection_status.is_right_connected.Assign(1); 459 }
460
461 pad_state.sampling_number =
462 npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
463 npad.joy_dual_lifo.WriteNextEntry(pad_state);
566 break; 464 break;
567 case NPadControllerType::JoyLeft: 465 case Core::HID::NpadStyleIndex::JoyconLeft:
568 left_entry.connection_status.raw = 0; 466 pad_state.connection_status.raw = 0;
569 left_entry.connection_status.is_connected.Assign(1); 467 pad_state.connection_status.is_connected.Assign(1);
570 left_entry.connection_status.is_left_connected.Assign(1); 468 pad_state.connection_status.is_left_connected.Assign(1);
571 left_entry.pad.pad_states.raw = pad_state.pad_states.raw; 469
572 left_entry.pad.l_stick = pad_state.l_stick; 470 libnx_state.connection_status.is_left_connected.Assign(1);
573 left_entry.pad.r_stick = pad_state.r_stick; 471 pad_state.sampling_number =
574 472 npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
575 libnx_entry.connection_status.is_left_connected.Assign(1); 473 npad.joy_left_lifo.WriteNextEntry(pad_state);
576 break; 474 break;
577 case NPadControllerType::JoyRight: 475 case Core::HID::NpadStyleIndex::JoyconRight:
578 right_entry.connection_status.raw = 0; 476 pad_state.connection_status.raw = 0;
579 right_entry.connection_status.is_connected.Assign(1); 477 pad_state.connection_status.is_connected.Assign(1);
580 right_entry.connection_status.is_right_connected.Assign(1); 478 pad_state.connection_status.is_right_connected.Assign(1);
581 right_entry.pad.pad_states.raw = pad_state.pad_states.raw; 479
582 right_entry.pad.l_stick = pad_state.l_stick; 480 libnx_state.connection_status.is_right_connected.Assign(1);
583 right_entry.pad.r_stick = pad_state.r_stick; 481 pad_state.sampling_number =
584 482 npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
585 libnx_entry.connection_status.is_right_connected.Assign(1); 483 npad.joy_right_lifo.WriteNextEntry(pad_state);
586 break; 484 break;
587 case NPadControllerType::GameCube: 485 case Core::HID::NpadStyleIndex::GameCube:
588 main_controller.connection_status.raw = 0; 486 pad_state.connection_status.raw = 0;
589 main_controller.connection_status.is_connected.Assign(1); 487 pad_state.connection_status.is_connected.Assign(1);
590 main_controller.connection_status.is_wired.Assign(1); 488 pad_state.connection_status.is_wired.Assign(1);
591 main_controller.pad.pad_states.raw = pad_state.pad_states.raw; 489
592 main_controller.pad.l_stick = pad_state.l_stick; 490 libnx_state.connection_status.is_wired.Assign(1);
593 main_controller.pad.r_stick = pad_state.r_stick; 491 pad_state.sampling_number =
594 trigger_entry.l_analog = trigger_state.l_analog; 492 npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
595 trigger_entry.r_analog = trigger_state.r_analog; 493 trigger_state.sampling_number =
596 494 npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
597 libnx_entry.connection_status.is_wired.Assign(1); 495 npad.fullkey_lifo.WriteNextEntry(pad_state);
496 npad.gc_trigger_lifo.WriteNextEntry(trigger_state);
598 break; 497 break;
599 case NPadControllerType::Pokeball: 498 case Core::HID::NpadStyleIndex::Pokeball:
600 pokeball_entry.connection_status.raw = 0; 499 pad_state.connection_status.raw = 0;
601 pokeball_entry.connection_status.is_connected.Assign(1); 500 pad_state.connection_status.is_connected.Assign(1);
602 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; 501 pad_state.sampling_number =
603 pokeball_entry.pad.l_stick = pad_state.l_stick; 502 npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
604 pokeball_entry.pad.r_stick = pad_state.r_stick; 503 npad.palma_lifo.WriteNextEntry(pad_state);
504 break;
505 default:
605 break; 506 break;
606 } 507 }
607 508
608 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 509 libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
609 // any controllers. 510 libnx_state.l_stick = pad_state.l_stick;
610 libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw; 511 libnx_state.r_stick = pad_state.r_stick;
611 libnx_entry.pad.l_stick = pad_state.l_stick; 512 npad.system_ext_lifo.WriteNextEntry(pad_state);
612 libnx_entry.pad.r_stick = pad_state.r_stick; 513
514 press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
613 515
614 press_state |= static_cast<u32>(pad_state.pad_states.raw); 516 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
517 &controller.shared_memory_entry, sizeof(NpadInternalState));
615 } 518 }
616 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
617 shared_memory_entries.size() * sizeof(NPadEntry));
618} 519}
619 520
620void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 521void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
@@ -622,145 +523,138 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
622 if (!IsControllerActivated()) { 523 if (!IsControllerActivated()) {
623 return; 524 return;
624 } 525 }
625 for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
626 auto& npad = shared_memory_entries[i];
627
628 const auto& controller_type = connected_controllers[i].type;
629 526
630 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { 527 for (std::size_t i = 0; i < controller_data.size(); ++i) {
631 continue; 528 auto& controller = controller_data[i];
632 }
633
634 const std::array<SixAxisGeneric*, 6> controller_sixaxes{
635 &npad.sixaxis_fullkey, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
636 &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
637 };
638
639 for (auto* sixaxis_sensor : controller_sixaxes) {
640 sixaxis_sensor->common.entry_count = 16;
641 sixaxis_sensor->common.total_entry_count = 17;
642
643 const auto& last_entry =
644 sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
645
646 sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
647 sixaxis_sensor->common.last_entry_index =
648 (sixaxis_sensor->common.last_entry_index + 1) % 17;
649 529
650 auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; 530 const auto& controller_type = controller.device->GetNpadStyleIndex();
651 531
652 cur_entry.timestamp = last_entry.timestamp + 1; 532 if (controller_type == Core::HID::NpadStyleIndex::None ||
653 cur_entry.timestamp2 = cur_entry.timestamp; 533 !controller.device->IsConnected()) {
534 continue;
654 } 535 }
655 536
656 // Try to read sixaxis sensor states 537 auto& npad = controller.shared_memory_entry;
657 std::array<MotionDevice, 2> motion_devices; 538 const auto& motion_state = controller.device->GetMotions();
658 539 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
659 if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) { 540 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
660 sixaxis_at_rest = true; 541 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
661 for (std::size_t e = 0; e < motion_devices.size(); ++e) { 542 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
662 const auto& device = motions[i][e]; 543 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
663 if (device) { 544 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
664 std::tie(motion_devices[e].accel, motion_devices[e].gyro, 545
665 motion_devices[e].rotation, motion_devices[e].orientation, 546 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
666 motion_devices[e].quaternion) = device->GetStatus(); 547 controller.sixaxis_at_rest = true;
667 sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f; 548 for (std::size_t e = 0; e < motion_state.size(); ++e) {
668 } 549 controller.sixaxis_at_rest =
550 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
669 } 551 }
670 } 552 }
671 553
672 auto& full_sixaxis_entry =
673 npad.sixaxis_fullkey.sixaxis[npad.sixaxis_fullkey.common.last_entry_index];
674 auto& handheld_sixaxis_entry =
675 npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
676 auto& dual_left_sixaxis_entry =
677 npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
678 auto& dual_right_sixaxis_entry =
679 npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
680 auto& left_sixaxis_entry =
681 npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
682 auto& right_sixaxis_entry =
683 npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
684
685 switch (controller_type) { 554 switch (controller_type) {
686 case NPadControllerType::None: 555 case Core::HID::NpadStyleIndex::None:
687 UNREACHABLE(); 556 UNREACHABLE();
688 break; 557 break;
689 case NPadControllerType::ProController: 558 case Core::HID::NpadStyleIndex::ProController:
690 full_sixaxis_entry.attribute.raw = 0; 559 sixaxis_fullkey_state.attribute.raw = 0;
691 if (sixaxis_sensors_enabled && motions[i][0]) { 560 if (controller.sixaxis_sensor_enabled) {
692 full_sixaxis_entry.attribute.is_connected.Assign(1); 561 sixaxis_fullkey_state.attribute.is_connected.Assign(1);
693 full_sixaxis_entry.accel = motion_devices[0].accel; 562 sixaxis_fullkey_state.accel = motion_state[0].accel;
694 full_sixaxis_entry.gyro = motion_devices[0].gyro; 563 sixaxis_fullkey_state.gyro = motion_state[0].gyro;
695 full_sixaxis_entry.rotation = motion_devices[0].rotation; 564 sixaxis_fullkey_state.rotation = motion_state[0].rotation;
696 full_sixaxis_entry.orientation = motion_devices[0].orientation; 565 sixaxis_fullkey_state.orientation = motion_state[0].orientation;
697 } 566 }
698 break; 567 break;
699 case NPadControllerType::Handheld: 568 case Core::HID::NpadStyleIndex::Handheld:
700 handheld_sixaxis_entry.attribute.raw = 0; 569 sixaxis_handheld_state.attribute.raw = 0;
701 if (sixaxis_sensors_enabled && motions[i][0]) { 570 if (controller.sixaxis_sensor_enabled) {
702 handheld_sixaxis_entry.attribute.is_connected.Assign(1); 571 sixaxis_handheld_state.attribute.is_connected.Assign(1);
703 handheld_sixaxis_entry.accel = motion_devices[0].accel; 572 sixaxis_handheld_state.accel = motion_state[0].accel;
704 handheld_sixaxis_entry.gyro = motion_devices[0].gyro; 573 sixaxis_handheld_state.gyro = motion_state[0].gyro;
705 handheld_sixaxis_entry.rotation = motion_devices[0].rotation; 574 sixaxis_handheld_state.rotation = motion_state[0].rotation;
706 handheld_sixaxis_entry.orientation = motion_devices[0].orientation; 575 sixaxis_handheld_state.orientation = motion_state[0].orientation;
707 } 576 }
708 break; 577 break;
709 case NPadControllerType::JoyDual: 578 case Core::HID::NpadStyleIndex::JoyconDual:
710 dual_left_sixaxis_entry.attribute.raw = 0; 579 sixaxis_dual_left_state.attribute.raw = 0;
711 dual_right_sixaxis_entry.attribute.raw = 0; 580 sixaxis_dual_right_state.attribute.raw = 0;
712 if (sixaxis_sensors_enabled && motions[i][0]) { 581 if (controller.sixaxis_sensor_enabled) {
713 // Set motion for the left joycon 582 // Set motion for the left joycon
714 dual_left_sixaxis_entry.attribute.is_connected.Assign(1); 583 sixaxis_dual_left_state.attribute.is_connected.Assign(1);
715 dual_left_sixaxis_entry.accel = motion_devices[0].accel; 584 sixaxis_dual_left_state.accel = motion_state[0].accel;
716 dual_left_sixaxis_entry.gyro = motion_devices[0].gyro; 585 sixaxis_dual_left_state.gyro = motion_state[0].gyro;
717 dual_left_sixaxis_entry.rotation = motion_devices[0].rotation; 586 sixaxis_dual_left_state.rotation = motion_state[0].rotation;
718 dual_left_sixaxis_entry.orientation = motion_devices[0].orientation; 587 sixaxis_dual_left_state.orientation = motion_state[0].orientation;
719 } 588 }
720 if (sixaxis_sensors_enabled && motions[i][1]) { 589 if (controller.sixaxis_sensor_enabled) {
721 // Set motion for the right joycon 590 // Set motion for the right joycon
722 dual_right_sixaxis_entry.attribute.is_connected.Assign(1); 591 sixaxis_dual_right_state.attribute.is_connected.Assign(1);
723 dual_right_sixaxis_entry.accel = motion_devices[1].accel; 592 sixaxis_dual_right_state.accel = motion_state[1].accel;
724 dual_right_sixaxis_entry.gyro = motion_devices[1].gyro; 593 sixaxis_dual_right_state.gyro = motion_state[1].gyro;
725 dual_right_sixaxis_entry.rotation = motion_devices[1].rotation; 594 sixaxis_dual_right_state.rotation = motion_state[1].rotation;
726 dual_right_sixaxis_entry.orientation = motion_devices[1].orientation; 595 sixaxis_dual_right_state.orientation = motion_state[1].orientation;
727 } 596 }
728 break; 597 break;
729 case NPadControllerType::JoyLeft: 598 case Core::HID::NpadStyleIndex::JoyconLeft:
730 left_sixaxis_entry.attribute.raw = 0; 599 sixaxis_left_lifo_state.attribute.raw = 0;
731 if (sixaxis_sensors_enabled && motions[i][0]) { 600 if (controller.sixaxis_sensor_enabled) {
732 left_sixaxis_entry.attribute.is_connected.Assign(1); 601 sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
733 left_sixaxis_entry.accel = motion_devices[0].accel; 602 sixaxis_left_lifo_state.accel = motion_state[0].accel;
734 left_sixaxis_entry.gyro = motion_devices[0].gyro; 603 sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
735 left_sixaxis_entry.rotation = motion_devices[0].rotation; 604 sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
736 left_sixaxis_entry.orientation = motion_devices[0].orientation; 605 sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
737 } 606 }
738 break; 607 break;
739 case NPadControllerType::JoyRight: 608 case Core::HID::NpadStyleIndex::JoyconRight:
740 right_sixaxis_entry.attribute.raw = 0; 609 sixaxis_right_lifo_state.attribute.raw = 0;
741 if (sixaxis_sensors_enabled && motions[i][1]) { 610 if (controller.sixaxis_sensor_enabled) {
742 right_sixaxis_entry.attribute.is_connected.Assign(1); 611 sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
743 right_sixaxis_entry.accel = motion_devices[1].accel; 612 sixaxis_right_lifo_state.accel = motion_state[1].accel;
744 right_sixaxis_entry.gyro = motion_devices[1].gyro; 613 sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
745 right_sixaxis_entry.rotation = motion_devices[1].rotation; 614 sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
746 right_sixaxis_entry.orientation = motion_devices[1].orientation; 615 sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
747 } 616 }
748 break; 617 break;
749 case NPadControllerType::GameCube: 618 default:
750 case NPadControllerType::Pokeball:
751 break; 619 break;
752 } 620 }
621
622 sixaxis_fullkey_state.sampling_number =
623 npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
624 sixaxis_handheld_state.sampling_number =
625 npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
626 sixaxis_dual_left_state.sampling_number =
627 npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
628 sixaxis_dual_right_state.sampling_number =
629 npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
630 sixaxis_left_lifo_state.sampling_number =
631 npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
632 sixaxis_right_lifo_state.sampling_number =
633 npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
634
635 if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
636 // This buffer only is updated on handheld on HW
637 npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
638 } else {
639 // Handheld doesn't update this buffer on HW
640 npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
641 }
642
643 npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
644 npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
645 npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
646 npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
647 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
648 &controller.shared_memory_entry, sizeof(NpadInternalState));
753 } 649 }
754 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
755 shared_memory_entries.size() * sizeof(NPadEntry));
756} 650}
757 651
758void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) { 652void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
759 style.raw = style_set.raw; 653 hid_core.SetSupportedStyleTag(style_set);
760} 654}
761 655
762Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const { 656Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
763 return style; 657 return hid_core.GetSupportedStyleTag();
764} 658}
765 659
766void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { 660void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
@@ -779,11 +673,11 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
779 return supported_npad_id_types.size(); 673 return supported_npad_id_types.size();
780} 674}
781 675
782void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { 676void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
783 hold_type = joy_hold_type; 677 hold_type = joy_hold_type;
784} 678}
785 679
786Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { 680Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
787 return hold_type; 681 return hold_type;
788} 682}
789 683
@@ -803,29 +697,91 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
803 return communication_mode; 697 return communication_mode;
804} 698}
805 699
806void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) { 700void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
807 const std::size_t npad_index = NPadIdToIndex(npad_id); 701 NpadJoyAssignmentMode assignment_mode) {
808 ASSERT(npad_index < shared_memory_entries.size()); 702 if (!IsNpadIdValid(npad_id)) {
809 if (shared_memory_entries[npad_index].assignment_mode != assignment_mode) { 703 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
810 shared_memory_entries[npad_index].assignment_mode = assignment_mode; 704 return;
705 }
706
707 auto& controller = GetControllerFromNpadIdType(npad_id);
708 if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
709 controller.shared_memory_entry.assignment_mode = assignment_mode;
710 }
711
712 if (!controller.device->IsConnected()) {
713 return;
714 }
715
716 if (assignment_mode == NpadJoyAssignmentMode::Dual) {
717 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
718 DisconnectNpad(npad_id);
719 controller.is_dual_left_connected = true;
720 controller.is_dual_right_connected = false;
721 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
722 return;
723 }
724 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
725 DisconnectNpad(npad_id);
726 controller.is_dual_left_connected = false;
727 controller.is_dual_right_connected = true;
728 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
729 return;
730 }
731 return;
732 }
733
734 // This is for NpadJoyAssignmentMode::Single
735
736 // Only JoyconDual get affected by this function
737 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
738 return;
739 }
740
741 if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
742 DisconnectNpad(npad_id);
743 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
744 return;
745 }
746 if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
747 DisconnectNpad(npad_id);
748 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
749 return;
750 }
751
752 // We have two controllers connected to the same npad_id we need to split them
753 const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId();
754 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
755 DisconnectNpad(npad_id);
756 if (npad_device_type == NpadJoyDeviceType::Left) {
757 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
758 controller_2.is_dual_left_connected = false;
759 controller_2.is_dual_right_connected = true;
760 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
761 } else {
762 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
763 controller_2.is_dual_left_connected = true;
764 controller_2.is_dual_right_connected = false;
765 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
811 } 766 }
812} 767}
813 768
814bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, 769bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
815 const VibrationValue& vibration_value) { 770 std::size_t device_index,
816 if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) { 771 const Core::HID::VibrationValue& vibration_value) {
772 auto& controller = GetControllerFromNpadIdType(npad_id);
773 if (!controller.device->IsConnected()) {
817 return false; 774 return false;
818 } 775 }
819 776
820 const auto& player = Settings::values.players.GetValue()[npad_index]; 777 if (!controller.device->IsVibrationEnabled()) {
821 778 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
822 if (!player.vibration_enabled) { 779 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
823 if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
824 latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
825 // Send an empty vibration to stop any vibrations. 780 // Send an empty vibration to stop any vibrations.
826 vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f); 781 Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
782 controller.device->SetVibration(device_index, vibration);
827 // Then reset the vibration value to its default value. 783 // Then reset the vibration value to its default value.
828 latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE; 784 controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE;
829 } 785 }
830 786
831 return false; 787 return false;
@@ -839,27 +795,25 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size
839 const auto now = steady_clock::now(); 795 const auto now = steady_clock::now();
840 796
841 // Filter out non-zero vibrations that are within 10ms of each other. 797 // Filter out non-zero vibrations that are within 10ms of each other.
842 if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) && 798 if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
843 duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) < 799 duration_cast<milliseconds>(
800 now - controller.vibration[device_index].last_vibration_timepoint) <
844 milliseconds(10)) { 801 milliseconds(10)) {
845 return false; 802 return false;
846 } 803 }
847 804
848 last_vibration_timepoints[npad_index][device_index] = now; 805 controller.vibration[device_index].last_vibration_timepoint = now;
849 } 806 }
850 807
851 auto& vibration = vibrations[npad_index][device_index]; 808 Core::HID::VibrationValue vibration{
852 const auto player_vibration_strength = static_cast<f32>(player.vibration_strength); 809 vibration_value.low_amplitude, vibration_value.low_frequency,
853 const auto amp_low = 810 vibration_value.high_amplitude, vibration_value.high_frequency};
854 std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f); 811 return controller.device->SetVibration(device_index, vibration);
855 const auto amp_high =
856 std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
857 return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
858 vibration_value.freq_high);
859} 812}
860 813
861void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, 814void Controller_NPad::VibrateController(
862 const VibrationValue& vibration_value) { 815 const Core::HID::VibrationDeviceHandle& vibration_device_handle,
816 const Core::HID::VibrationValue& vibration_value) {
863 if (!IsDeviceHandleValid(vibration_device_handle)) { 817 if (!IsDeviceHandleValid(vibration_device_handle)) {
864 return; 818 return;
865 } 819 }
@@ -868,42 +822,45 @@ void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_han
868 return; 822 return;
869 } 823 }
870 824
871 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 825 auto& controller = GetControllerFromHandle(vibration_device_handle);
872 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 826 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
873 827
874 if (!vibration_devices_mounted[npad_index][device_index] || 828 if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
875 !connected_controllers[npad_index].is_connected) {
876 return; 829 return;
877 } 830 }
878 831
879 if (vibration_device_handle.device_index == DeviceIndex::None) { 832 if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
880 UNREACHABLE_MSG("DeviceIndex should never be None!"); 833 UNREACHABLE_MSG("DeviceIndex should never be None!");
881 return; 834 return;
882 } 835 }
883 836
884 // Some games try to send mismatched parameters in the device handle, block these. 837 // Some games try to send mismatched parameters in the device handle, block these.
885 if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && 838 if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
886 (vibration_device_handle.npad_type == NpadType::JoyconRight || 839 (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight ||
887 vibration_device_handle.device_index == DeviceIndex::Right)) || 840 vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) ||
888 (connected_controllers[npad_index].type == NPadControllerType::JoyRight && 841 (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight &&
889 (vibration_device_handle.npad_type == NpadType::JoyconLeft || 842 (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft ||
890 vibration_device_handle.device_index == DeviceIndex::Left))) { 843 vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) {
891 return; 844 return;
892 } 845 }
893 846
894 // Filter out vibrations with equivalent values to reduce unnecessary state changes. 847 // Filter out vibrations with equivalent values to reduce unnecessary state changes.
895 if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low && 848 if (vibration_value.low_amplitude ==
896 vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) { 849 controller.vibration[device_index].latest_vibration_value.low_amplitude &&
850 vibration_value.high_amplitude ==
851 controller.vibration[device_index].latest_vibration_value.high_amplitude) {
897 return; 852 return;
898 } 853 }
899 854
900 if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) { 855 if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index,
901 latest_vibration_values[npad_index][device_index] = vibration_value; 856 vibration_value)) {
857 controller.vibration[device_index].latest_vibration_value = vibration_value;
902 } 858 }
903} 859}
904 860
905void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, 861void Controller_NPad::VibrateControllers(
906 const std::vector<VibrationValue>& vibration_values) { 862 const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
863 const std::vector<Core::HID::VibrationValue>& vibration_values) {
907 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { 864 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
908 return; 865 return;
909 } 866 }
@@ -918,168 +875,285 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat
918 } 875 }
919} 876}
920 877
921Controller_NPad::VibrationValue Controller_NPad::GetLastVibration( 878Core::HID::VibrationValue Controller_NPad::GetLastVibration(
922 const DeviceHandle& vibration_device_handle) const { 879 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
923 if (!IsDeviceHandleValid(vibration_device_handle)) { 880 if (!IsDeviceHandleValid(vibration_device_handle)) {
924 return {}; 881 return {};
925 } 882 }
926 883
927 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 884 const auto& controller = GetControllerFromHandle(vibration_device_handle);
928 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 885 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
929 return latest_vibration_values[npad_index][device_index]; 886 return controller.vibration[device_index].latest_vibration_value;
930} 887}
931 888
932void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) { 889void Controller_NPad::InitializeVibrationDevice(
890 const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
933 if (!IsDeviceHandleValid(vibration_device_handle)) { 891 if (!IsDeviceHandleValid(vibration_device_handle)) {
934 return; 892 return;
935 } 893 }
936 894
937 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 895 const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
938 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 896 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
939 InitializeVibrationDeviceAtIndex(npad_index, device_index); 897 InitializeVibrationDeviceAtIndex(npad_index, device_index);
940} 898}
941 899
942void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, 900void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
943 std::size_t device_index) { 901 std::size_t device_index) {
902 auto& controller = GetControllerFromNpadIdType(npad_id);
944 if (!Settings::values.vibration_enabled.GetValue()) { 903 if (!Settings::values.vibration_enabled.GetValue()) {
945 vibration_devices_mounted[npad_index][device_index] = false; 904 controller.vibration[device_index].device_mounted = false;
946 return; 905 return;
947 } 906 }
948 907
949 if (vibrations[npad_index][device_index]) { 908 controller.vibration[device_index].device_mounted =
950 vibration_devices_mounted[npad_index][device_index] = 909 controller.device->TestVibration(device_index);
951 vibrations[npad_index][device_index]->GetStatus() == 1;
952 } else {
953 vibration_devices_mounted[npad_index][device_index] = false;
954 }
955} 910}
956 911
957void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 912void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
958 permit_vibration_session_enabled = permit_vibration_session; 913 permit_vibration_session_enabled = permit_vibration_session;
959} 914}
960 915
961bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const { 916bool Controller_NPad::IsVibrationDeviceMounted(
917 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
962 if (!IsDeviceHandleValid(vibration_device_handle)) { 918 if (!IsDeviceHandleValid(vibration_device_handle)) {
963 return false; 919 return false;
964 } 920 }
965 921
966 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 922 const auto& controller = GetControllerFromHandle(vibration_device_handle);
967 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 923 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
968 return vibration_devices_mounted[npad_index][device_index]; 924 return controller.vibration[device_index].device_mounted;
969} 925}
970 926
971Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) { 927Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
972 return styleset_changed_events[NPadIdToIndex(npad_id)]->GetReadableEvent(); 928 if (!IsNpadIdValid(npad_id)) {
929 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
930 // Fallback to player 1
931 const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1);
932 return controller.styleset_changed_event->GetReadableEvent();
933 }
934
935 const auto& controller = GetControllerFromNpadIdType(npad_id);
936 return controller.styleset_changed_event->GetReadableEvent();
973} 937}
974 938
975void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const { 939void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
976 styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent().Signal(); 940 const auto& controller = GetControllerFromNpadIdType(npad_id);
941 controller.styleset_changed_event->GetWritableEvent().Signal();
977} 942}
978 943
979void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { 944void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
980 UpdateControllerAt(controller, npad_index, true); 945 Core::HID::NpadIdType npad_id) {
946 UpdateControllerAt(controller, npad_id, true);
981} 947}
982 948
983void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, 949void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
984 bool connected) { 950 Core::HID::NpadIdType npad_id, bool connected) {
951 auto& controller = GetControllerFromNpadIdType(npad_id);
985 if (!connected) { 952 if (!connected) {
986 DisconnectNpadAtIndex(npad_index); 953 DisconnectNpad(npad_id);
987 return;
988 }
989
990 if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) {
991 Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
992 MapNPadToSettingsType(controller);
993 Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
994 connected_controllers[HANDHELD_INDEX] = {controller, true};
995 InitNewlyAddedController(HANDHELD_INDEX);
996 return; 954 return;
997 } 955 }
998 956
999 Settings::values.players.GetValue()[npad_index].controller_type = 957 controller.device->SetNpadStyleIndex(type);
1000 MapNPadToSettingsType(controller); 958 InitNewlyAddedController(npad_id);
1001 Settings::values.players.GetValue()[npad_index].connected = true;
1002 connected_controllers[npad_index] = {controller, true};
1003 InitNewlyAddedController(npad_index);
1004} 959}
1005 960
1006void Controller_NPad::DisconnectNpad(u32 npad_id) { 961void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1007 DisconnectNpadAtIndex(NPadIdToIndex(npad_id)); 962 if (!IsNpadIdValid(npad_id)) {
1008} 963 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
964 return;
965 }
1009 966
1010void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) { 967 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
1011 for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) { 968 auto& controller = GetControllerFromNpadIdType(npad_id);
969 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
1012 // Send an empty vibration to stop any vibrations. 970 // Send an empty vibration to stop any vibrations.
1013 VibrateControllerAtIndex(npad_index, device_idx, {}); 971 VibrateControllerAtIndex(npad_id, device_idx, {});
1014 vibration_devices_mounted[npad_index][device_idx] = false; 972 controller.vibration[device_idx].device_mounted = false;
1015 } 973 }
1016 974
1017 Settings::values.players.GetValue()[npad_index].connected = false; 975 auto& shared_memory_entry = controller.shared_memory_entry;
1018 connected_controllers[npad_index].is_connected = false; 976 // Don't reset shared_memory_entry.assignment_mode this value is persistent
1019 977 shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
1020 auto& controller = shared_memory_entries[npad_index]; 978 shared_memory_entry.device_type.raw = 0;
1021 controller.style_set.raw = 0; // Zero out 979 shared_memory_entry.system_properties.raw = 0;
1022 controller.device_type.raw = 0; 980 shared_memory_entry.button_properties.raw = 0;
1023 controller.system_properties.raw = 0; 981 shared_memory_entry.battery_level_dual = 0;
1024 controller.button_properties.raw = 0; 982 shared_memory_entry.battery_level_left = 0;
1025 controller.battery_level_dual = 0; 983 shared_memory_entry.battery_level_right = 0;
1026 controller.battery_level_left = 0; 984 shared_memory_entry.fullkey_color = {
1027 controller.battery_level_right = 0; 985 .attribute = ColorAttribute::NoController,
1028 controller.fullkey_color = {}; 986 .fullkey = {},
1029 controller.joycon_color = {}; 987 };
1030 controller.assignment_mode = NpadAssignments::Dual; 988 shared_memory_entry.joycon_color = {
1031 controller.footer_type = AppletFooterUiType::None; 989 .attribute = ColorAttribute::NoController,
990 .left = {},
991 .right = {},
992 };
993 shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
994
995 controller.is_dual_left_connected = true;
996 controller.is_dual_right_connected = true;
997 controller.is_connected = false;
998 controller.device->Disconnect();
999 SignalStyleSetChangedEvent(npad_id);
1000 WriteEmptyEntry(controller.shared_memory_entry);
1001}
1032 1002
1033 SignalStyleSetChangedEvent(IndexToNPad(npad_index)); 1003void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
1004 GyroscopeZeroDriftMode drift_mode) {
1005 if (!IsDeviceHandleValid(sixaxis_handle)) {
1006 LOG_ERROR(Service_HID, "Invalid handle");
1007 return;
1008 }
1009 auto& controller = GetControllerFromHandle(sixaxis_handle);
1010 controller.gyroscope_zero_drift_mode = drift_mode;
1034} 1011}
1035 1012
1036void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 1013Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode(
1037 gyroscope_zero_drift_mode = drift_mode; 1014 Core::HID::SixAxisSensorHandle sixaxis_handle) const {
1015 if (!IsDeviceHandleValid(sixaxis_handle)) {
1016 LOG_ERROR(Service_HID, "Invalid handle");
1017 // Return the default value
1018 return GyroscopeZeroDriftMode::Standard;
1019 }
1020 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1021 return controller.gyroscope_zero_drift_mode;
1038} 1022}
1039 1023
1040Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const { 1024bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const {
1041 return gyroscope_zero_drift_mode; 1025 if (!IsDeviceHandleValid(sixaxis_handle)) {
1026 LOG_ERROR(Service_HID, "Invalid handle");
1027 // Return the default value
1028 return true;
1029 }
1030 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1031 return controller.sixaxis_at_rest;
1042} 1032}
1043 1033
1044bool Controller_NPad::IsSixAxisSensorAtRest() const { 1034void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1045 return sixaxis_at_rest; 1035 bool sixaxis_status) {
1036 if (!IsDeviceHandleValid(sixaxis_handle)) {
1037 LOG_ERROR(Service_HID, "Invalid handle");
1038 return;
1039 }
1040 auto& controller = GetControllerFromHandle(sixaxis_handle);
1041 controller.sixaxis_sensor_enabled = sixaxis_status;
1046} 1042}
1047 1043
1048void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) { 1044void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1049 sixaxis_sensors_enabled = six_axis_status; 1045 bool sixaxis_fusion_status) {
1046 if (!IsDeviceHandleValid(sixaxis_handle)) {
1047 LOG_ERROR(Service_HID, "Invalid handle");
1048 return;
1049 }
1050 auto& controller = GetControllerFromHandle(sixaxis_handle);
1051 controller.sixaxis_fusion_enabled = sixaxis_fusion_status;
1050} 1052}
1051 1053
1052void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) { 1054void Controller_NPad::SetSixAxisFusionParameters(
1053 sixaxis_fusion_parameter1 = parameter1; 1055 Core::HID::SixAxisSensorHandle sixaxis_handle,
1054 sixaxis_fusion_parameter2 = parameter2; 1056 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1057 if (!IsDeviceHandleValid(sixaxis_handle)) {
1058 LOG_ERROR(Service_HID, "Invalid handle");
1059 return;
1060 }
1061 auto& controller = GetControllerFromHandle(sixaxis_handle);
1062 controller.sixaxis_fusion = sixaxis_fusion_parameters;
1055} 1063}
1056 1064
1057std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() { 1065Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters(
1058 return { 1066 Core::HID::SixAxisSensorHandle sixaxis_handle) {
1059 sixaxis_fusion_parameter1, 1067 if (!IsDeviceHandleValid(sixaxis_handle)) {
1060 sixaxis_fusion_parameter2, 1068 LOG_ERROR(Service_HID, "Invalid handle");
1061 }; 1069 // Since these parameters are unknow just return zeros
1070 return {};
1071 }
1072 auto& controller = GetControllerFromHandle(sixaxis_handle);
1073 return controller.sixaxis_fusion;
1062} 1074}
1063 1075
1064void Controller_NPad::ResetSixAxisFusionParameters() { 1076void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) {
1065 sixaxis_fusion_parameter1 = 0.0f; 1077 if (!IsDeviceHandleValid(sixaxis_handle)) {
1066 sixaxis_fusion_parameter2 = 0.0f; 1078 LOG_ERROR(Service_HID, "Invalid handle");
1079 return;
1080 }
1081 auto& controller = GetControllerFromHandle(sixaxis_handle);
1082 // Since these parameters are unknow just fill with zeros
1083 controller.sixaxis_fusion = {};
1067} 1084}
1068 1085
1069void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { 1086void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1070 const auto npad_index_1 = NPadIdToIndex(npad_id_1); 1087 Core::HID::NpadIdType npad_id_2) {
1071 const auto npad_index_2 = NPadIdToIndex(npad_id_2); 1088 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1089 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1090 npad_id_2);
1091 return;
1092 }
1093 auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
1094 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
1095 const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
1096 const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
1097 bool merge_controllers = false;
1072 1098
1073 // If the controllers at both npad indices form a pair of left and right joycons, merge them. 1099 // If the controllers at both npad indices form a pair of left and right joycons, merge them.
1074 // Otherwise, do nothing. 1100 // Otherwise, do nothing.
1075 if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft && 1101 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
1076 connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) || 1102 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
1077 (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && 1103 merge_controllers = true;
1078 connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { 1104 }
1105 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
1106 controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
1107 merge_controllers = true;
1108 }
1109 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1110 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
1111 controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
1112 merge_controllers = true;
1113 }
1114 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1115 controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
1116 !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
1117 merge_controllers = true;
1118 }
1119 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1120 controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
1121 controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1122 merge_controllers = true;
1123 }
1124 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1125 controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
1126 !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1127 merge_controllers = true;
1128 }
1129 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1130 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1131 controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
1132 !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1133 merge_controllers = true;
1134 }
1135 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1136 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1137 !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
1138 controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1139 merge_controllers = true;
1140 }
1141
1142 if (merge_controllers) {
1079 // Disconnect the joycon at the second id and connect the dual joycon at the first index. 1143 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
1080 DisconnectNpad(npad_id_2); 1144 DisconnectNpad(npad_id_2);
1081 AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); 1145 controller_1.is_dual_left_connected = true;
1146 controller_1.is_dual_right_connected = true;
1147 AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
1148 return;
1082 } 1149 }
1150 LOG_WARNING(Service_HID,
1151 "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
1152 "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
1153 npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
1154 controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
1155 controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
1156 controller_2.is_dual_right_connected);
1083} 1157}
1084 1158
1085void Controller_NPad::StartLRAssignmentMode() { 1159void Controller_NPad::StartLRAssignmentMode() {
@@ -1092,61 +1166,66 @@ void Controller_NPad::StopLRAssignmentMode() {
1092 is_in_lr_assignment_mode = false; 1166 is_in_lr_assignment_mode = false;
1093} 1167}
1094 1168
1095bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { 1169bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1096 if (npad_id_1 == NPAD_HANDHELD || npad_id_2 == NPAD_HANDHELD || npad_id_1 == NPAD_UNKNOWN || 1170 Core::HID::NpadIdType npad_id_2) {
1097 npad_id_2 == NPAD_UNKNOWN) { 1171 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1172 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1173 npad_id_2);
1174 return false;
1175 }
1176 if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
1177 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
1178 npad_id_2 == Core::HID::NpadIdType::Other) {
1098 return true; 1179 return true;
1099 } 1180 }
1100 const auto npad_index_1 = NPadIdToIndex(npad_id_1); 1181 const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
1101 const auto npad_index_2 = NPadIdToIndex(npad_id_2); 1182 const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
1102 1183 const auto type_index_1 = controller_1->GetNpadStyleIndex();
1103 if (!IsControllerSupported(connected_controllers[npad_index_1].type) || 1184 const auto type_index_2 = controller_2->GetNpadStyleIndex();
1104 !IsControllerSupported(connected_controllers[npad_index_2].type)) { 1185 const auto is_connected_1 = controller_1->IsConnected();
1186 const auto is_connected_2 = controller_2->IsConnected();
1187
1188 if (!IsControllerSupported(type_index_1) && is_connected_1) {
1189 return false;
1190 }
1191 if (!IsControllerSupported(type_index_2) && is_connected_2) {
1105 return false; 1192 return false;
1106 } 1193 }
1107 1194
1108 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); 1195 UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
1109 1196 UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
1110 AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
1111 AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
1112 1197
1113 return true; 1198 return true;
1114} 1199}
1115 1200
1116Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 1201Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
1117 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { 1202 if (!IsNpadIdValid(npad_id)) {
1118 // These are controllers without led patterns 1203 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1119 return LedPattern{0, 0, 0, 0}; 1204 return Core::HID::LedPattern{0, 0, 0, 0};
1120 }
1121 switch (npad_id) {
1122 case 0:
1123 return LedPattern{1, 0, 0, 0};
1124 case 1:
1125 return LedPattern{1, 1, 0, 0};
1126 case 2:
1127 return LedPattern{1, 1, 1, 0};
1128 case 3:
1129 return LedPattern{1, 1, 1, 1};
1130 case 4:
1131 return LedPattern{1, 0, 0, 1};
1132 case 5:
1133 return LedPattern{1, 0, 1, 0};
1134 case 6:
1135 return LedPattern{1, 0, 1, 1};
1136 case 7:
1137 return LedPattern{0, 1, 1, 0};
1138 default:
1139 return LedPattern{0, 0, 0, 0};
1140 } 1205 }
1206 const auto& controller = GetControllerFromNpadIdType(npad_id).device;
1207 return controller->GetLedPattern();
1141} 1208}
1142 1209
1143bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const { 1210bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
1144 return unintended_home_button_input_protection[NPadIdToIndex(npad_id)]; 1211 Core::HID::NpadIdType npad_id) const {
1212 if (!IsNpadIdValid(npad_id)) {
1213 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1214 // Return the default value
1215 return false;
1216 }
1217 const auto& controller = GetControllerFromNpadIdType(npad_id);
1218 return controller.unintended_home_button_input_protection;
1145} 1219}
1146 1220
1147void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 1221void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
1148 u32 npad_id) { 1222 Core::HID::NpadIdType npad_id) {
1149 unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; 1223 if (!IsNpadIdValid(npad_id)) {
1224 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1225 return;
1226 }
1227 auto& controller = GetControllerFromNpadIdType(npad_id);
1228 controller.unintended_home_button_input_protection = is_protection_enabled;
1150} 1229}
1151 1230
1152void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1231void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1154,49 +1233,51 @@ void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
1154} 1233}
1155 1234
1156void Controller_NPad::ClearAllConnectedControllers() { 1235void Controller_NPad::ClearAllConnectedControllers() {
1157 for (auto& controller : connected_controllers) { 1236 for (auto& controller : controller_data) {
1158 if (controller.is_connected && controller.type != NPadControllerType::None) { 1237 if (controller.device->IsConnected() &&
1159 controller.type = NPadControllerType::None; 1238 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
1160 controller.is_connected = false; 1239 controller.device->Disconnect();
1240 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1161 } 1241 }
1162 } 1242 }
1163} 1243}
1164 1244
1165void Controller_NPad::DisconnectAllConnectedControllers() { 1245void Controller_NPad::DisconnectAllConnectedControllers() {
1166 for (auto& controller : connected_controllers) { 1246 for (auto& controller : controller_data) {
1167 controller.is_connected = false; 1247 controller.device->Disconnect();
1168 } 1248 }
1169} 1249}
1170 1250
1171void Controller_NPad::ConnectAllDisconnectedControllers() { 1251void Controller_NPad::ConnectAllDisconnectedControllers() {
1172 for (auto& controller : connected_controllers) { 1252 for (auto& controller : controller_data) {
1173 if (controller.type != NPadControllerType::None && !controller.is_connected) { 1253 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
1174 controller.is_connected = true; 1254 !controller.device->IsConnected()) {
1255 controller.device->Connect();
1175 } 1256 }
1176 } 1257 }
1177} 1258}
1178 1259
1179void Controller_NPad::ClearAllControllers() { 1260void Controller_NPad::ClearAllControllers() {
1180 for (auto& controller : connected_controllers) { 1261 for (auto& controller : controller_data) {
1181 controller.type = NPadControllerType::None; 1262 controller.device->Disconnect();
1182 controller.is_connected = false; 1263 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1183 } 1264 }
1184} 1265}
1185 1266
1186u32 Controller_NPad::GetAndResetPressState() { 1267Core::HID::NpadButton Controller_NPad::GetAndResetPressState() {
1187 return press_state.exchange(0); 1268 return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
1188} 1269}
1189 1270
1190bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { 1271bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
1191 if (controller == NPadControllerType::Handheld) { 1272 if (controller == Core::HID::NpadStyleIndex::Handheld) {
1192 const bool support_handheld = 1273 const bool support_handheld =
1193 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1274 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
1194 NPAD_HANDHELD) != supported_npad_id_types.end(); 1275 Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end();
1195 // Handheld is not even a supported type, lets stop here 1276 // Handheld is not even a supported type, lets stop here
1196 if (!support_handheld) { 1277 if (!support_handheld) {
1197 return false; 1278 return false;
1198 } 1279 }
1199 // Handheld should not be supported in docked mode 1280 // Handheld shouldn't be supported in docked mode
1200 if (Settings::values.use_docked_mode.GetValue()) { 1281 if (Settings::values.use_docked_mode.GetValue()) {
1201 return false; 1282 return false;
1202 } 1283 }
@@ -1205,20 +1286,31 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
1205 } 1286 }
1206 1287
1207 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1288 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
1208 [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) { 1289 [](Core::HID::NpadIdType npad_id) {
1290 return npad_id <= Core::HID::NpadIdType::Player8;
1291 })) {
1292 Core::HID::NpadStyleTag style = GetSupportedStyleSet();
1209 switch (controller) { 1293 switch (controller) {
1210 case NPadControllerType::ProController: 1294 case Core::HID::NpadStyleIndex::ProController:
1211 return style.fullkey; 1295 return style.fullkey;
1212 case NPadControllerType::JoyDual: 1296 case Core::HID::NpadStyleIndex::JoyconDual:
1213 return style.joycon_dual; 1297 return style.joycon_dual;
1214 case NPadControllerType::JoyLeft: 1298 case Core::HID::NpadStyleIndex::JoyconLeft:
1215 return style.joycon_left; 1299 return style.joycon_left;
1216 case NPadControllerType::JoyRight: 1300 case Core::HID::NpadStyleIndex::JoyconRight:
1217 return style.joycon_right; 1301 return style.joycon_right;
1218 case NPadControllerType::GameCube: 1302 case Core::HID::NpadStyleIndex::GameCube:
1219 return style.gamecube; 1303 return style.gamecube;
1220 case NPadControllerType::Pokeball: 1304 case Core::HID::NpadStyleIndex::Pokeball:
1221 return style.palma; 1305 return style.palma;
1306 case Core::HID::NpadStyleIndex::NES:
1307 return style.lark;
1308 case Core::HID::NpadStyleIndex::SNES:
1309 return style.lucia;
1310 case Core::HID::NpadStyleIndex::N64:
1311 return style.lagoon;
1312 case Core::HID::NpadStyleIndex::SegaGenesis:
1313 return style.lager;
1222 default: 1314 default:
1223 return false; 1315 return false;
1224 } 1316 }
@@ -1227,4 +1319,48 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
1227 return false; 1319 return false;
1228} 1320}
1229 1321
1322Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1323 const Core::HID::SixAxisSensorHandle& device_handle) {
1324 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1325 return GetControllerFromNpadIdType(npad_id);
1326}
1327
1328const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1329 const Core::HID::SixAxisSensorHandle& device_handle) const {
1330 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1331 return GetControllerFromNpadIdType(npad_id);
1332}
1333
1334Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1335 const Core::HID::VibrationDeviceHandle& device_handle) {
1336 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1337 return GetControllerFromNpadIdType(npad_id);
1338}
1339
1340const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1341 const Core::HID::VibrationDeviceHandle& device_handle) const {
1342 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1343 return GetControllerFromNpadIdType(npad_id);
1344}
1345
1346Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
1347 Core::HID::NpadIdType npad_id) {
1348 if (!IsNpadIdValid(npad_id)) {
1349 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1350 npad_id = Core::HID::NpadIdType::Player1;
1351 }
1352 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
1353 return controller_data[npad_index];
1354}
1355
1356const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
1357 Core::HID::NpadIdType npad_id) const {
1358 if (!IsNpadIdValid(npad_id)) {
1359 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1360 npad_id = Core::HID::NpadIdType::Player1;
1361 }
1362 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
1363 return controller_data[npad_index];
1364}
1365
1230} // namespace Service::HID 1366} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 9ee146caf..63281cb35 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -11,9 +11,14 @@
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/quaternion.h" 13#include "common/quaternion.h"
14#include "common/settings.h" 14#include "core/hid/hid_types.h"
15#include "core/frontend/input.h"
16#include "core/hle/service/hid/controllers/controller_base.h" 15#include "core/hle/service/hid/controllers/controller_base.h"
16#include "core/hle/service/hid/ring_lifo.h"
17
18namespace Core::HID {
19class EmulatedController;
20enum class ControllerTriggerType;
21} // namespace Core::HID
17 22
18namespace Kernel { 23namespace Kernel {
19class KEvent; 24class KEvent;
@@ -26,12 +31,9 @@ class ServiceContext;
26 31
27namespace Service::HID { 32namespace Service::HID {
28 33
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31
32class Controller_NPad final : public ControllerBase { 34class Controller_NPad final : public ControllerBase {
33public: 35public:
34 explicit Controller_NPad(Core::System& system_, 36 explicit Controller_NPad(Core::HID::HIDCore& hid_core_,
35 KernelHelpers::ServiceContext& service_context_); 37 KernelHelpers::ServiceContext& service_context_);
36 ~Controller_NPad() override; 38 ~Controller_NPad() override;
37 39
@@ -48,60 +50,39 @@ public:
48 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 50 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
49 std::size_t size) override; 51 std::size_t size) override;
50 52
51 // Called when input devices should be loaded 53 // This is nn::hid::GyroscopeZeroDriftMode
52 void OnLoadInputDevices() override;
53
54 enum class NPadControllerType {
55 None,
56 ProController,
57 Handheld,
58 JoyDual,
59 JoyLeft,
60 JoyRight,
61 GameCube,
62 Pokeball,
63 };
64
65 enum class NpadType : u8 {
66 ProController = 3,
67 Handheld = 4,
68 JoyconDual = 5,
69 JoyconLeft = 6,
70 JoyconRight = 7,
71 GameCube = 8,
72 Pokeball = 9,
73 MaxNpadType = 10,
74 };
75
76 enum class DeviceIndex : u8 {
77 Left = 0,
78 Right = 1,
79 None = 2,
80 MaxDeviceIndex = 3,
81 };
82
83 enum class GyroscopeZeroDriftMode : u32 { 54 enum class GyroscopeZeroDriftMode : u32 {
84 Loose = 0, 55 Loose = 0,
85 Standard = 1, 56 Standard = 1,
86 Tight = 2, 57 Tight = 2,
87 }; 58 };
88 59
89 enum class NpadHoldType : u64 { 60 // This is nn::hid::NpadJoyHoldType
61 enum class NpadJoyHoldType : u64 {
90 Vertical = 0, 62 Vertical = 0,
91 Horizontal = 1, 63 Horizontal = 1,
92 }; 64 };
93 65
94 enum class NpadAssignments : u32 { 66 // This is nn::hid::NpadJoyAssignmentMode
67 enum class NpadJoyAssignmentMode : u32 {
95 Dual = 0, 68 Dual = 0,
96 Single = 1, 69 Single = 1,
97 }; 70 };
98 71
72 // This is nn::hid::NpadJoyDeviceType
73 enum class NpadJoyDeviceType : s64 {
74 Left = 0,
75 Right = 1,
76 };
77
78 // This is nn::hid::NpadHandheldActivationMode
99 enum class NpadHandheldActivationMode : u64 { 79 enum class NpadHandheldActivationMode : u64 {
100 Dual = 0, 80 Dual = 0,
101 Single = 1, 81 Single = 1,
102 None = 2, 82 None = 2,
103 }; 83 };
104 84
85 // This is nn::hid::NpadCommunicationMode
105 enum class NpadCommunicationMode : u64 { 86 enum class NpadCommunicationMode : u64 {
106 Mode_5ms = 0, 87 Mode_5ms = 0,
107 Mode_10ms = 1, 88 Mode_10ms = 1,
@@ -109,74 +90,22 @@ public:
109 Default = 3, 90 Default = 3,
110 }; 91 };
111 92
112 struct DeviceHandle { 93 static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
113 NpadType npad_type; 94 .low_amplitude = 0.0f,
114 u8 npad_id; 95 .low_frequency = 160.0f,
115 DeviceIndex device_index; 96 .high_amplitude = 0.0f,
116 INSERT_PADDING_BYTES_NOINIT(1); 97 .high_frequency = 320.0f,
117 }; 98 };
118 static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
119 99
120 struct NpadStyleSet { 100 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
121 union { 101 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
122 u32_le raw{};
123
124 BitField<0, 1, u32> fullkey;
125 BitField<1, 1, u32> handheld;
126 BitField<2, 1, u32> joycon_dual;
127 BitField<3, 1, u32> joycon_left;
128 BitField<4, 1, u32> joycon_right;
129 BitField<5, 1, u32> gamecube;
130 BitField<6, 1, u32> palma;
131 BitField<7, 1, u32> lark;
132 BitField<8, 1, u32> handheld_lark;
133 BitField<9, 1, u32> lucia;
134 BitField<29, 1, u32> system_ext;
135 BitField<30, 1, u32> system;
136 };
137 };
138 static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
139
140 struct VibrationValue {
141 f32 amp_low;
142 f32 freq_low;
143 f32 amp_high;
144 f32 freq_high;
145 };
146 static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
147
148 static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
149 .amp_low = 0.0f,
150 .freq_low = 160.0f,
151 .amp_high = 0.0f,
152 .freq_high = 320.0f,
153 };
154
155 struct LedPattern {
156 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
157 position1.Assign(light1);
158 position2.Assign(light2);
159 position3.Assign(light3);
160 position4.Assign(light4);
161 }
162 union {
163 u64 raw{};
164 BitField<0, 1, u64> position1;
165 BitField<1, 1, u64> position2;
166 BitField<2, 1, u64> position3;
167 BitField<3, 1, u64> position4;
168 };
169 };
170
171 void SetSupportedStyleSet(NpadStyleSet style_set);
172 NpadStyleSet GetSupportedStyleSet() const;
173 102
174 void SetSupportedNpadIdTypes(u8* data, std::size_t length); 103 void SetSupportedNpadIdTypes(u8* data, std::size_t length);
175 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 104 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
176 std::size_t GetSupportedNpadIdTypesSize() const; 105 std::size_t GetSupportedNpadIdTypesSize() const;
177 106
178 void SetHoldType(NpadHoldType joy_hold_type); 107 void SetHoldType(NpadJoyHoldType joy_hold_type);
179 NpadHoldType GetHoldType() const; 108 NpadJoyHoldType GetHoldType() const;
180 109
181 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); 110 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
182 NpadHandheldActivationMode GetNpadHandheldActivationMode() const; 111 NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
@@ -184,162 +113,107 @@ public:
184 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); 113 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
185 NpadCommunicationMode GetNpadCommunicationMode() const; 114 NpadCommunicationMode GetNpadCommunicationMode() const;
186 115
187 void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); 116 void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
117 NpadJoyAssignmentMode assignment_mode);
188 118
189 bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, 119 bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
190 const VibrationValue& vibration_value); 120 const Core::HID::VibrationValue& vibration_value);
191 121
192 void VibrateController(const DeviceHandle& vibration_device_handle, 122 void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
193 const VibrationValue& vibration_value); 123 const Core::HID::VibrationValue& vibration_value);
194 124
195 void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, 125 void VibrateControllers(
196 const std::vector<VibrationValue>& vibration_values); 126 const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
127 const std::vector<Core::HID::VibrationValue>& vibration_values);
197 128
198 VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const; 129 Core::HID::VibrationValue GetLastVibration(
130 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
199 131
200 void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle); 132 void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
201 133
202 void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index); 134 void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index);
203 135
204 void SetPermitVibrationSession(bool permit_vibration_session); 136 void SetPermitVibrationSession(bool permit_vibration_session);
205 137
206 bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const; 138 bool IsVibrationDeviceMounted(
139 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
207 140
208 Kernel::KReadableEvent& GetStyleSetChangedEvent(u32 npad_id); 141 Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id);
209 void SignalStyleSetChangedEvent(u32 npad_id) const; 142 void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const;
210 143
211 // Adds a new controller at an index. 144 // Adds a new controller at an index.
212 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); 145 void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id);
213 // Adds a new controller at an index with connection status. 146 // Adds a new controller at an index with connection status.
214 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); 147 void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
215 148 bool connected);
216 void DisconnectNpad(u32 npad_id); 149
217 void DisconnectNpadAtIndex(std::size_t index); 150 void DisconnectNpad(Core::HID::NpadIdType npad_id);
218 151
219 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 152 void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
220 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 153 GyroscopeZeroDriftMode drift_mode);
221 bool IsSixAxisSensorAtRest() const; 154 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode(
222 void SetSixAxisEnabled(bool six_axis_status); 155 Core::HID::SixAxisSensorHandle sixaxis_handle) const;
223 void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2); 156 bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const;
224 std::pair<f32, f32> GetSixAxisFusionParameters(); 157 void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status);
225 void ResetSixAxisFusionParameters(); 158 void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
226 LedPattern GetLedPattern(u32 npad_id); 159 bool sixaxis_fusion_status);
227 bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; 160 void SetSixAxisFusionParameters(
228 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); 161 Core::HID::SixAxisSensorHandle sixaxis_handle,
162 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
163 Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters(
164 Core::HID::SixAxisSensorHandle sixaxis_handle);
165 void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle);
166 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
167 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
168 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
169 Core::HID::NpadIdType npad_id);
229 void SetAnalogStickUseCenterClamp(bool use_center_clamp); 170 void SetAnalogStickUseCenterClamp(bool use_center_clamp);
230 void ClearAllConnectedControllers(); 171 void ClearAllConnectedControllers();
231 void DisconnectAllConnectedControllers(); 172 void DisconnectAllConnectedControllers();
232 void ConnectAllDisconnectedControllers(); 173 void ConnectAllDisconnectedControllers();
233 void ClearAllControllers(); 174 void ClearAllControllers();
234 175
235 void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2); 176 void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
236 void StartLRAssignmentMode(); 177 void StartLRAssignmentMode();
237 void StopLRAssignmentMode(); 178 void StopLRAssignmentMode();
238 bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); 179 bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
239 180
240 // Logical OR for all buttons presses on all controllers 181 // Logical OR for all buttons presses on all controllers
241 // Specifically for cheat engine and other features. 182 // Specifically for cheat engine and other features.
242 u32 GetAndResetPressState(); 183 Core::HID::NpadButton GetAndResetPressState();
243 184
244 static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type); 185 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
245 static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type); 186 static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
246 static std::size_t NPadIdToIndex(u32 npad_id); 187 static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
247 static u32 IndexToNPad(std::size_t index);
248 static bool IsNpadIdValid(u32 npad_id);
249 static bool IsDeviceHandleValid(const DeviceHandle& device_handle);
250 188
251private: 189private:
252 struct CommonHeader { 190 // This is nn::hid::detail::ColorAttribute
253 s64_le timestamp; 191 enum class ColorAttribute : u32 {
254 s64_le total_entry_count;
255 s64_le last_entry_index;
256 s64_le entry_count;
257 };
258 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
259
260 enum class ColorAttributes : u32_le {
261 Ok = 0, 192 Ok = 0,
262 ReadError = 1, 193 ReadError = 1,
263 NoController = 2, 194 NoController = 2,
264 }; 195 };
265 static_assert(sizeof(ColorAttributes) == 4, "ColorAttributes is an invalid size"); 196 static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
266 197
267 struct ControllerColor { 198 // This is nn::hid::detail::NpadFullKeyColorState
268 u32_le body; 199 struct NpadFullKeyColorState {
269 u32_le button; 200 ColorAttribute attribute;
201 Core::HID::NpadControllerColor fullkey;
270 }; 202 };
271 static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size"); 203 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
272 204
273 struct FullKeyColor { 205 // This is nn::hid::detail::NpadJoyColorState
274 ColorAttributes attribute; 206 struct NpadJoyColorState {
275 ControllerColor fullkey; 207 ColorAttribute attribute;
208 Core::HID::NpadControllerColor left;
209 Core::HID::NpadControllerColor right;
276 }; 210 };
277 static_assert(sizeof(FullKeyColor) == 0xC, "FullKeyColor is an invalid size"); 211 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
278 212
279 struct JoyconColor { 213 // This is nn::hid::NpadAttribute
280 ColorAttributes attribute; 214 struct NpadAttribute {
281 ControllerColor left;
282 ControllerColor right;
283 };
284 static_assert(sizeof(JoyconColor) == 0x14, "JoyconColor is an invalid size");
285
286 struct ControllerPadState {
287 union {
288 u64_le raw{};
289 // Button states
290 BitField<0, 1, u64> a;
291 BitField<1, 1, u64> b;
292 BitField<2, 1, u64> x;
293 BitField<3, 1, u64> y;
294 BitField<4, 1, u64> l_stick;
295 BitField<5, 1, u64> r_stick;
296 BitField<6, 1, u64> l;
297 BitField<7, 1, u64> r;
298 BitField<8, 1, u64> zl;
299 BitField<9, 1, u64> zr;
300 BitField<10, 1, u64> plus;
301 BitField<11, 1, u64> minus;
302
303 // D-Pad
304 BitField<12, 1, u64> d_left;
305 BitField<13, 1, u64> d_up;
306 BitField<14, 1, u64> d_right;
307 BitField<15, 1, u64> d_down;
308
309 // Left JoyStick
310 BitField<16, 1, u64> l_stick_left;
311 BitField<17, 1, u64> l_stick_up;
312 BitField<18, 1, u64> l_stick_right;
313 BitField<19, 1, u64> l_stick_down;
314
315 // Right JoyStick
316 BitField<20, 1, u64> r_stick_left;
317 BitField<21, 1, u64> r_stick_up;
318 BitField<22, 1, u64> r_stick_right;
319 BitField<23, 1, u64> r_stick_down;
320
321 // Not always active?
322 BitField<24, 1, u64> left_sl;
323 BitField<25, 1, u64> left_sr;
324
325 BitField<26, 1, u64> right_sl;
326 BitField<27, 1, u64> right_sr;
327
328 BitField<28, 1, u64> palma;
329 BitField<30, 1, u64> handheld_left_b;
330 };
331 };
332 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
333
334 struct AnalogPosition {
335 s32_le x;
336 s32_le y;
337 };
338 static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
339
340 struct ConnectionState {
341 union { 215 union {
342 u32_le raw{}; 216 u32 raw{};
343 BitField<0, 1, u32> is_connected; 217 BitField<0, 1, u32> is_connected;
344 BitField<1, 1, u32> is_wired; 218 BitField<1, 1, u32> is_wired;
345 BitField<2, 1, u32> is_left_connected; 219 BitField<2, 1, u32> is_left_connected;
@@ -348,79 +222,60 @@ private:
348 BitField<5, 1, u32> is_right_wired; 222 BitField<5, 1, u32> is_right_wired;
349 }; 223 };
350 }; 224 };
351 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); 225 static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
352 226
353 struct ControllerPad { 227 // This is nn::hid::NpadFullKeyState
354 ControllerPadState pad_states; 228 // This is nn::hid::NpadHandheldState
355 AnalogPosition l_stick; 229 // This is nn::hid::NpadJoyDualState
356 AnalogPosition r_stick; 230 // This is nn::hid::NpadJoyLeftState
357 }; 231 // This is nn::hid::NpadJoyRightState
358 static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size"); 232 // This is nn::hid::NpadPalmaState
359 233 // This is nn::hid::NpadSystemExtState
360 struct GenericStates { 234 struct NPadGenericState {
361 s64_le timestamp; 235 s64_le sampling_number;
362 s64_le timestamp2; 236 Core::HID::NpadButtonState npad_buttons;
363 ControllerPad pad; 237 Core::HID::AnalogStickState l_stick;
364 ConnectionState connection_status; 238 Core::HID::AnalogStickState r_stick;
365 }; 239 NpadAttribute connection_status;
366 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); 240 INSERT_PADDING_BYTES(4); // Reserved
367
368 struct NPadGeneric {
369 CommonHeader common;
370 std::array<GenericStates, 17> npad;
371 }; 241 };
372 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); 242 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
373 243
374 struct SixAxisAttributes { 244 // This is nn::hid::SixAxisSensorAttribute
245 struct SixAxisSensorAttribute {
375 union { 246 union {
376 u32_le raw{}; 247 u32 raw{};
377 BitField<0, 1, u32> is_connected; 248 BitField<0, 1, u32> is_connected;
378 BitField<1, 1, u32> is_interpolated; 249 BitField<1, 1, u32> is_interpolated;
379 }; 250 };
380 }; 251 };
381 static_assert(sizeof(SixAxisAttributes) == 4, "SixAxisAttributes is an invalid size"); 252 static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
382 253
383 struct SixAxisStates { 254 // This is nn::hid::SixAxisSensorState
384 s64_le timestamp{}; 255 struct SixAxisSensorState {
385 INSERT_PADDING_WORDS(2); 256 s64 delta_time{};
386 s64_le timestamp2{}; 257 s64 sampling_number{};
387 Common::Vec3f accel{}; 258 Common::Vec3f accel{};
388 Common::Vec3f gyro{}; 259 Common::Vec3f gyro{};
389 Common::Vec3f rotation{}; 260 Common::Vec3f rotation{};
390 std::array<Common::Vec3f, 3> orientation{}; 261 std::array<Common::Vec3f, 3> orientation{};
391 SixAxisAttributes attribute; 262 SixAxisSensorAttribute attribute;
392 INSERT_PADDING_BYTES(4); // Reserved 263 INSERT_PADDING_BYTES(4); // Reserved
393 }; 264 };
394 static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size"); 265 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
395
396 struct SixAxisGeneric {
397 CommonHeader common{};
398 std::array<SixAxisStates, 17> sixaxis{};
399 };
400 static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
401 266
402 struct TriggerState { 267 // This is nn::hid::server::NpadGcTriggerState
403 s64_le timestamp{}; 268 struct NpadGcTriggerState {
404 s64_le timestamp2{}; 269 s64 sampling_number{};
405 s32_le l_analog{}; 270 s32 l_analog{};
406 s32_le r_analog{}; 271 s32 r_analog{};
407 }; 272 };
408 static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size"); 273 static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
409
410 struct TriggerGeneric {
411 INSERT_PADDING_BYTES(0x4);
412 s64_le timestamp;
413 INSERT_PADDING_BYTES(0x4);
414 s64_le total_entry_count;
415 s64_le last_entry_index;
416 s64_le entry_count;
417 std::array<TriggerState, 17> trigger{};
418 };
419 static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
420 274
275 // This is nn::hid::NpadSystemProperties
421 struct NPadSystemProperties { 276 struct NPadSystemProperties {
422 union { 277 union {
423 s64_le raw{}; 278 s64 raw{};
424 BitField<0, 1, s64> is_charging_joy_dual; 279 BitField<0, 1, s64> is_charging_joy_dual;
425 BitField<1, 1, s64> is_charging_joy_left; 280 BitField<1, 1, s64> is_charging_joy_left;
426 BitField<2, 1, s64> is_charging_joy_right; 281 BitField<2, 1, s64> is_charging_joy_right;
@@ -438,17 +293,20 @@ private:
438 }; 293 };
439 static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size"); 294 static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
440 295
441 struct NPadButtonProperties { 296 // This is nn::hid::NpadSystemButtonProperties
297 struct NpadSystemButtonProperties {
442 union { 298 union {
443 s32_le raw{}; 299 s32 raw{};
444 BitField<0, 1, s32> is_home_button_protection_enabled; 300 BitField<0, 1, s32> is_home_button_protection_enabled;
445 }; 301 };
446 }; 302 };
447 static_assert(sizeof(NPadButtonProperties) == 0x4, "NPadButtonProperties is an invalid size"); 303 static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
304 "NPadButtonProperties is an invalid size");
448 305
449 struct NPadDevice { 306 // This is nn::hid::system::DeviceType
307 struct DeviceType {
450 union { 308 union {
451 u32_le raw{}; 309 u32 raw{};
452 BitField<0, 1, s32> fullkey; 310 BitField<0, 1, s32> fullkey;
453 BitField<1, 1, s32> debug_pad; 311 BitField<1, 1, s32> debug_pad;
454 BitField<2, 1, s32> handheld_left; 312 BitField<2, 1, s32> handheld_left;
@@ -465,26 +323,29 @@ private:
465 BitField<13, 1, s32> handheld_lark_nes_left; 323 BitField<13, 1, s32> handheld_lark_nes_left;
466 BitField<14, 1, s32> handheld_lark_nes_right; 324 BitField<14, 1, s32> handheld_lark_nes_right;
467 BitField<15, 1, s32> lucia; 325 BitField<15, 1, s32> lucia;
326 BitField<16, 1, s32> lagon;
327 BitField<17, 1, s32> lager;
468 BitField<31, 1, s32> system; 328 BitField<31, 1, s32> system;
469 }; 329 };
470 }; 330 };
471 331
472 struct MotionDevice { 332 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
473 Common::Vec3f accel; 333 struct NfcXcdDeviceHandleStateImpl {
474 Common::Vec3f gyro; 334 u64 handle;
475 Common::Vec3f rotation; 335 bool is_available;
476 std::array<Common::Vec3f, 3> orientation; 336 bool is_activated;
477 Common::Quaternion<f32> quaternion; 337 INSERT_PADDING_BYTES(0x6); // Reserved
478 }; 338 u64 sampling_number;
479
480 struct NfcXcdHandle {
481 INSERT_PADDING_BYTES(0x60);
482 }; 339 };
340 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
341 "NfcXcdDeviceHandleStateImpl is an invalid size");
483 342
343 // This is nn::hid::system::AppletFooterUiAttributesSet
484 struct AppletFooterUiAttributes { 344 struct AppletFooterUiAttributes {
485 INSERT_PADDING_BYTES(0x4); 345 INSERT_PADDING_BYTES(0x4);
486 }; 346 };
487 347
348 // This is nn::hid::system::AppletFooterUiType
488 enum class AppletFooterUiType : u8 { 349 enum class AppletFooterUiType : u8 {
489 None = 0, 350 None = 0,
490 HandheldNone = 1, 351 HandheldNone = 1,
@@ -510,95 +371,153 @@ private:
510 Lagon = 21, 371 Lagon = 21,
511 }; 372 };
512 373
513 struct NPadEntry { 374 struct AppletFooterUi {
514 NpadStyleSet style_set; 375 AppletFooterUiAttributes attributes;
515 NpadAssignments assignment_mode; 376 AppletFooterUiType type;
516 FullKeyColor fullkey_color; 377 INSERT_PADDING_BYTES(0x5B); // Reserved
517 JoyconColor joycon_color; 378 };
518 379 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
519 NPadGeneric fullkey_states; 380
520 NPadGeneric handheld_states; 381 // This is nn::hid::NpadLarkType
521 NPadGeneric joy_dual_states; 382 enum class NpadLarkType : u32 {
522 NPadGeneric joy_left_states; 383 Invalid,
523 NPadGeneric joy_right_states; 384 H1,
524 NPadGeneric palma_states; 385 H2,
525 NPadGeneric system_ext_states; 386 NL,
526 SixAxisGeneric sixaxis_fullkey; 387 NR,
527 SixAxisGeneric sixaxis_handheld; 388 };
528 SixAxisGeneric sixaxis_dual_left; 389
529 SixAxisGeneric sixaxis_dual_right; 390 // This is nn::hid::NpadLuciaType
530 SixAxisGeneric sixaxis_left; 391 enum class NpadLuciaType : u32 {
531 SixAxisGeneric sixaxis_right; 392 Invalid,
532 NPadDevice device_type; 393 J,
533 INSERT_PADDING_BYTES(0x4); // reserved 394 E,
395 U,
396 };
397
398 // This is nn::hid::NpadLagonType
399 enum class NpadLagonType : u32 {
400 Invalid,
401 };
402
403 // This is nn::hid::NpadLagerType
404 enum class NpadLagerType : u32 {
405 Invalid,
406 J,
407 E,
408 U,
409 };
410
411 // This is nn::hid::detail::NpadInternalState
412 struct NpadInternalState {
413 Core::HID::NpadStyleTag style_tag;
414 NpadJoyAssignmentMode assignment_mode;
415 NpadFullKeyColorState fullkey_color;
416 NpadJoyColorState joycon_color;
417 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
418 Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
419 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
420 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
421 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
422 Lifo<NPadGenericState, hid_entry_count> palma_lifo;
423 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
424 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
425 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
426 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
427 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
428 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
429 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
430 DeviceType device_type;
431 INSERT_PADDING_BYTES(0x4); // Reserved
534 NPadSystemProperties system_properties; 432 NPadSystemProperties system_properties;
535 NPadButtonProperties button_properties; 433 NpadSystemButtonProperties button_properties;
536 u32 battery_level_dual; 434 Core::HID::NpadBatteryLevel battery_level_dual;
537 u32 battery_level_left; 435 Core::HID::NpadBatteryLevel battery_level_left;
538 u32 battery_level_right; 436 Core::HID::NpadBatteryLevel battery_level_right;
539 AppletFooterUiAttributes footer_attributes; 437 union {
540 AppletFooterUiType footer_type; 438 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
541 // nfc_states needs to be checked switchbrew does not match with HW 439 AppletFooterUi applet_footer;
542 NfcXcdHandle nfc_states; 440 };
543 INSERT_PADDING_BYTES(0x8); // Mutex 441 INSERT_PADDING_BYTES(0x20); // Unknown
544 TriggerGeneric gc_trigger_states; 442 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
545 INSERT_PADDING_BYTES(0xc1f); 443 NpadLarkType lark_type_l_and_main;
546 }; 444 NpadLarkType lark_type_r;
547 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); 445 NpadLuciaType lucia_type;
548 446 NpadLagonType lagon_type;
549 struct ControllerHolder { 447 NpadLagerType lager_type;
550 NPadControllerType type; 448 // FW 13.x Investigate there is some sort of bitflag related to joycons
551 bool is_connected; 449 INSERT_PADDING_BYTES(0x4);
552 }; 450 INSERT_PADDING_BYTES(0xc08); // Unknown
553 451 };
554 void InitNewlyAddedController(std::size_t controller_idx); 452 static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
555 bool IsControllerSupported(NPadControllerType controller) const; 453
556 void RequestPadStateUpdate(u32 npad_id); 454 struct VibrationData {
557 455 bool device_mounted{};
558 std::atomic<u32> press_state{}; 456 Core::HID::VibrationValue latest_vibration_value{};
559 457 std::chrono::steady_clock::time_point last_vibration_timepoint{};
560 NpadStyleSet style{}; 458 };
561 std::array<NPadEntry, 10> shared_memory_entries{}; 459
562 using ButtonArray = std::array< 460 struct NpadControllerData {
563 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, 461 Core::HID::EmulatedController* device;
564 10>; 462 Kernel::KEvent* styleset_changed_event{};
565 using StickArray = std::array< 463 NpadInternalState shared_memory_entry{};
566 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, 464
567 10>; 465 std::array<VibrationData, 2> vibration{};
568 using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>, 466 bool unintended_home_button_input_protection{};
569 Settings::NativeVibration::NUM_VIBRATIONS_HID>, 467 bool is_connected{};
570 10>; 468
571 using MotionArray = std::array< 469 // Dual joycons can have only one side connected
572 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>, 470 bool is_dual_left_connected{true};
573 10>; 471 bool is_dual_right_connected{true};
574 472
473 // Motion parameters
474 bool sixaxis_at_rest{true};
475 bool sixaxis_sensor_enabled{true};
476 bool sixaxis_fusion_enabled{false};
477 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{};
478 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
479
480 // Current pad state
481 NPadGenericState npad_pad_state{};
482 NPadGenericState npad_libnx_state{};
483 NpadGcTriggerState npad_trigger_state{};
484 SixAxisSensorState sixaxis_fullkey_state{};
485 SixAxisSensorState sixaxis_handheld_state{};
486 SixAxisSensorState sixaxis_dual_left_state{};
487 SixAxisSensorState sixaxis_dual_right_state{};
488 SixAxisSensorState sixaxis_left_lifo_state{};
489 SixAxisSensorState sixaxis_right_lifo_state{};
490 int callback_key;
491 };
492
493 void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
494 void InitNewlyAddedController(Core::HID::NpadIdType npad_id);
495 bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;
496 void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
497 void WriteEmptyEntry(NpadInternalState& npad);
498
499 NpadControllerData& GetControllerFromHandle(
500 const Core::HID::SixAxisSensorHandle& device_handle);
501 const NpadControllerData& GetControllerFromHandle(
502 const Core::HID::SixAxisSensorHandle& device_handle) const;
503 NpadControllerData& GetControllerFromHandle(
504 const Core::HID::VibrationDeviceHandle& device_handle);
505 const NpadControllerData& GetControllerFromHandle(
506 const Core::HID::VibrationDeviceHandle& device_handle) const;
507 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
508 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
509
510 std::atomic<u64> press_state{};
511
512 std::array<NpadControllerData, 10> controller_data{};
575 KernelHelpers::ServiceContext& service_context; 513 KernelHelpers::ServiceContext& service_context;
576 std::mutex mutex; 514 std::mutex mutex;
577 ButtonArray buttons; 515 std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
578 StickArray sticks; 516 NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical};
579 VibrationArray vibrations;
580 MotionArray motions;
581 std::vector<u32> supported_npad_id_types{};
582 NpadHoldType hold_type{NpadHoldType::Vertical};
583 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 517 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
584 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 518 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
585 // Each controller should have their own styleset changed event
586 std::array<Kernel::KEvent*, 10> styleset_changed_events{};
587 std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10>
588 last_vibration_timepoints{};
589 std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
590 bool permit_vibration_session_enabled{false}; 519 bool permit_vibration_session_enabled{false};
591 std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
592 std::array<ControllerHolder, 10> connected_controllers{};
593 std::array<bool, 10> unintended_home_button_input_protection{};
594 bool analog_stick_use_center_clamp{}; 520 bool analog_stick_use_center_clamp{};
595 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
596 bool sixaxis_sensors_enabled{true};
597 f32 sixaxis_fusion_parameter1{};
598 f32 sixaxis_fusion_parameter2{};
599 bool sixaxis_at_rest{true};
600 std::array<ControllerPad, 10> npad_pad_states{};
601 std::array<TriggerState, 10> npad_trigger_states{};
602 bool is_in_lr_assignment_mode{false}; 521 bool is_in_lr_assignment_mode{false};
603}; 522};
604} // namespace Service::HID 523} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 772c20453..b7d7a5756 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -5,11 +5,12 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/stubbed.h" 9#include "core/hle/service/hid/controllers/stubbed.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11 12
12Controller_Stubbed::Controller_Stubbed(Core::System& system_) : ControllerBase{system_} {} 13Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
13Controller_Stubbed::~Controller_Stubbed() = default; 14Controller_Stubbed::~Controller_Stubbed() = default;
14 15
15void Controller_Stubbed::OnInit() {} 16void Controller_Stubbed::OnInit() {}
@@ -31,10 +32,9 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
31 std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); 32 std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
32} 33}
33 34
34void Controller_Stubbed::OnLoadInputDevices() {}
35
36void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { 35void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
37 common_offset = off; 36 common_offset = off;
38 smart_update = true; 37 smart_update = true;
39} 38}
39
40} // namespace Service::HID 40} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 21092af0d..0044a4efa 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -10,7 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase { 11class Controller_Stubbed final : public ControllerBase {
12public: 12public:
13 explicit Controller_Stubbed(Core::System& system_); 13 explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_);
14 ~Controller_Stubbed() override; 14 ~Controller_Stubbed() override;
15 15
16 // Called when the controller is initialized 16 // Called when the controller is initialized
@@ -22,12 +22,17 @@ public:
22 // When the controller is requesting an update for the shared memory 22 // When the controller is requesting an update for the shared memory
23 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 23 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
24 24
25 // Called when input devices should be loaded
26 void OnLoadInputDevices() override;
27
28 void SetCommonHeaderOffset(std::size_t off); 25 void SetCommonHeaderOffset(std::size_t off);
29 26
30private: 27private:
28 struct CommonHeader {
29 s64 timestamp;
30 s64 total_entry_count;
31 s64 last_entry_index;
32 s64 entry_count;
33 };
34 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
35
31 bool smart_update{}; 36 bool smart_update{};
32 std::size_t common_offset{}; 37 std::size_t common_offset{};
33}; 38};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 6ef17acc5..48978e5c6 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -7,72 +7,82 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 13#include "core/hid/emulated_console.h"
14#include "core/hid/hid_core.h"
13#include "core/hle/service/hid/controllers/touchscreen.h" 15#include "core/hle/service/hid/controllers/touchscreen.h"
14 16
15namespace Service::HID { 17namespace Service::HID {
16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 18constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
17 19
18Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {} 20Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_)
21 : ControllerBase{hid_core_} {
22 console = hid_core.GetEmulatedConsole();
23}
24
19Controller_Touchscreen::~Controller_Touchscreen() = default; 25Controller_Touchscreen::~Controller_Touchscreen() = default;
20 26
21void Controller_Touchscreen::OnInit() { 27void Controller_Touchscreen::OnInit() {}
22 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
23 mouse_finger_id[id] = MAX_FINGERS;
24 keyboard_finger_id[id] = MAX_FINGERS;
25 udp_finger_id[id] = MAX_FINGERS;
26 }
27}
28 28
29void Controller_Touchscreen::OnRelease() {} 29void Controller_Touchscreen::OnRelease() {}
30 30
31void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 31void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
32 std::size_t size) { 32 std::size_t size) {
33 shared_memory.header.timestamp = core_timing.GetCPUTicks(); 33 touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
34 shared_memory.header.total_entry_count = 17;
35 34
36 if (!IsControllerActivated()) { 35 if (!IsControllerActivated()) {
37 shared_memory.header.entry_count = 0; 36 touch_screen_lifo.buffer_count = 0;
38 shared_memory.header.last_entry_index = 0; 37 touch_screen_lifo.buffer_tail = 0;
38 std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo));
39 return; 39 return;
40 } 40 }
41 shared_memory.header.entry_count = 16;
42 41
43 const auto& last_entry = 42 const auto touch_status = console->GetTouch();
44 shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; 43 for (std::size_t id = 0; id < MAX_FINGERS; id++) {
45 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 44 const auto& current_touch = touch_status[id];
46 auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; 45 auto& finger = fingers[id];
46 finger.position = current_touch.position;
47 finger.id = current_touch.id;
47 48
48 cur_entry.sampling_number = last_entry.sampling_number + 1; 49 if (finger.attribute.start_touch) {
49 cur_entry.sampling_number2 = cur_entry.sampling_number; 50 finger.attribute.raw = 0;
51 continue;
52 }
50 53
51 const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); 54 if (finger.attribute.end_touch) {
52 const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); 55 finger.attribute.raw = 0;
53 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 56 finger.pressed = false;
54 mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); 57 continue;
55 udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); 58 }
56 } 59
60 if (!finger.pressed && current_touch.pressed) {
61 finger.attribute.start_touch.Assign(1);
62 finger.pressed = true;
63 continue;
64 }
57 65
58 if (Settings::values.use_touch_from_button) { 66 if (finger.pressed && !current_touch.pressed) {
59 const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus(); 67 finger.attribute.raw = 0;
60 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 68 finger.attribute.end_touch.Assign(1);
61 keyboard_finger_id[id] =
62 UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
63 } 69 }
64 } 70 }
65 71
66 std::array<Finger, 16> active_fingers; 72 std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
67 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 73 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
68 [](const auto& finger) { return finger.pressed; }); 74 [](const auto& finger) { return finger.pressed; });
69 const auto active_fingers_count = 75 const auto active_fingers_count =
70 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 76 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
71 77
72 const u64 tick = core_timing.GetCPUTicks(); 78 const u64 tick = core_timing.GetCPUTicks();
73 cur_entry.entry_count = static_cast<s32_le>(active_fingers_count); 79 const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state;
80
81 next_state.sampling_number = last_entry.sampling_number + 1;
82 next_state.entry_count = static_cast<s32>(active_fingers_count);
83
74 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 84 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
75 auto& touch_entry = cur_entry.states[id]; 85 auto& touch_entry = next_state.states[id];
76 if (id < active_fingers_count) { 86 if (id < active_fingers_count) {
77 const auto& [active_x, active_y] = active_fingers[id].position; 87 const auto& [active_x, active_y] = active_fingers[id].position;
78 touch_entry.position = { 88 touch_entry.position = {
@@ -97,66 +107,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
97 touch_entry.finger = 0; 107 touch_entry.finger = 0;
98 } 108 }
99 } 109 }
100 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
101}
102
103void Controller_Touchscreen::OnLoadInputDevices() {
104 touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
105 touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
106 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
107}
108
109std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
110 // Dont assign any touch input to a finger if disabled
111 if (!Settings::values.touchscreen.enabled) {
112 return std::nullopt;
113 }
114 std::size_t first_free_id = 0;
115 while (first_free_id < MAX_FINGERS) {
116 if (!fingers[first_free_id].pressed) {
117 return first_free_id;
118 } else {
119 first_free_id++;
120 }
121 }
122 return std::nullopt;
123}
124
125std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
126 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
127 const auto& [x, y, pressed] = touch_input;
128 if (finger_id > MAX_FINGERS) {
129 LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
130 return MAX_FINGERS;
131 }
132 if (pressed) {
133 Attributes attribute{};
134 if (finger_id == MAX_FINGERS) {
135 const auto first_free_id = GetUnusedFingerID();
136 if (!first_free_id) {
137 // Invalid finger id do nothing
138 return MAX_FINGERS;
139 }
140 finger_id = first_free_id.value();
141 fingers[finger_id].pressed = true;
142 fingers[finger_id].id = static_cast<u32_le>(finger_id);
143 attribute.start_touch.Assign(1);
144 }
145 fingers[finger_id].position = {x, y};
146 fingers[finger_id].attribute = attribute;
147 return finger_id;
148 }
149
150 if (finger_id != MAX_FINGERS) {
151 if (!fingers[finger_id].attribute.end_touch) {
152 fingers[finger_id].attribute.end_touch.Assign(1);
153 fingers[finger_id].attribute.start_touch.Assign(0);
154 return finger_id;
155 }
156 fingers[finger_id].pressed = false;
157 }
158 110
159 return MAX_FINGERS; 111 touch_screen_lifo.WriteNextEntry(next_state);
112 std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo));
160} 113}
161 114
162} // namespace Service::HID 115} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 8e9b40c0a..708dde4f0 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -9,18 +9,25 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h" 10#include "common/point.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/frontend/input.h" 12#include "core/hid/hid_types.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/hle/service/hid/ring_lifo.h"
15
16namespace Core::HID {
17class EmulatedConsole;
18} // namespace Core::HID
14 19
15namespace Service::HID { 20namespace Service::HID {
16class Controller_Touchscreen final : public ControllerBase { 21class Controller_Touchscreen final : public ControllerBase {
17public: 22public:
23 // This is nn::hid::TouchScreenModeForNx
18 enum class TouchScreenModeForNx : u8 { 24 enum class TouchScreenModeForNx : u8 {
19 UseSystemSetting, 25 UseSystemSetting,
20 Finger, 26 Finger,
21 Heat2, 27 Heat2,
22 }; 28 };
23 29
30 // This is nn::hid::TouchScreenConfigurationForNx
24 struct TouchScreenConfigurationForNx { 31 struct TouchScreenConfigurationForNx {
25 TouchScreenModeForNx mode; 32 TouchScreenModeForNx mode;
26 INSERT_PADDING_BYTES_NOINIT(0x7); 33 INSERT_PADDING_BYTES_NOINIT(0x7);
@@ -29,7 +36,7 @@ public:
29 static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17, 36 static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
30 "TouchScreenConfigurationForNx is an invalid size"); 37 "TouchScreenConfigurationForNx is an invalid size");
31 38
32 explicit Controller_Touchscreen(Core::System& system_); 39 explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_);
33 ~Controller_Touchscreen() override; 40 ~Controller_Touchscreen() override;
34 41
35 // Called when the controller is initialized 42 // Called when the controller is initialized
@@ -41,73 +48,24 @@ public:
41 // When the controller is requesting an update for the shared memory 48 // When the controller is requesting an update for the shared memory
42 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 49 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
43 50
44 // Called when input devices should be loaded
45 void OnLoadInputDevices() override;
46
47private: 51private:
48 static constexpr std::size_t MAX_FINGERS = 16; 52 static constexpr std::size_t MAX_FINGERS = 16;
49 53
50 // Returns an unused finger id, if there is no fingers available std::nullopt will be returned 54 // This is nn::hid::TouchScreenState
51 std::optional<std::size_t> GetUnusedFingerID() const; 55 struct TouchScreenState {
52 56 s64 sampling_number;
53 // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no 57 s32 entry_count;
54 // changes will be made. Updates the coordinates if the finger id it's already set. If the touch 58 INSERT_PADDING_BYTES(4); // Reserved
55 // ends delays the output by one frame to set the end_touch flag before finally freeing the 59 std::array<Core::HID::TouchState, MAX_FINGERS> states;
56 // finger id
57 std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
58 std::size_t finger_id);
59
60 struct Attributes {
61 union {
62 u32 raw{};
63 BitField<0, 1, u32> start_touch;
64 BitField<1, 1, u32> end_touch;
65 };
66 };
67 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
68
69 struct TouchState {
70 u64_le delta_time;
71 Attributes attribute;
72 u32_le finger;
73 Common::Point<u32_le> position;
74 u32_le diameter_x;
75 u32_le diameter_y;
76 u32_le rotation_angle;
77 }; 60 };
78 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); 61 static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
79 62
80 struct TouchScreenEntry { 63 // This is nn::hid::detail::TouchScreenLifo
81 s64_le sampling_number; 64 Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
82 s64_le sampling_number2; 65 static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
83 s32_le entry_count; 66 TouchScreenState next_state{};
84 std::array<TouchState, MAX_FINGERS> states;
85 };
86 static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
87
88 struct TouchScreenSharedMemory {
89 CommonHeader header;
90 std::array<TouchScreenEntry, 17> shared_memory_entries{};
91 INSERT_PADDING_BYTES(0x3c8);
92 };
93 static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
94 "TouchScreenSharedMemory is an invalid size");
95
96 struct Finger {
97 u64_le last_touch{};
98 Common::Point<float> position;
99 u32_le id{};
100 bool pressed{};
101 Attributes attribute;
102 };
103 67
104 TouchScreenSharedMemory shared_memory{}; 68 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers;
105 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 69 Core::HID::EmulatedConsole* console;
106 std::unique_ptr<Input::TouchDevice> touch_udp_device;
107 std::unique_ptr<Input::TouchDevice> touch_btn_device;
108 std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
109 std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
110 std::array<std::size_t, MAX_FINGERS> udp_finger_id;
111 std::array<Finger, MAX_FINGERS> fingers;
112}; 70};
113} // namespace Service::HID 71} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 41dc22cf9..e4da16466 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -5,12 +5,13 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/xpad.h" 9#include "core/hle/service/hid/controllers/xpad.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 13
13Controller_XPad::Controller_XPad(Core::System& system_) : ControllerBase{system_} {} 14Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
14Controller_XPad::~Controller_XPad() = default; 15Controller_XPad::~Controller_XPad() = default;
15 16
16void Controller_XPad::OnInit() {} 17void Controller_XPad::OnInit() {}
@@ -19,28 +20,19 @@ void Controller_XPad::OnRelease() {}
19 20
20void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 21void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 22 std::size_t size) {
22 for (auto& xpad_entry : shared_memory.shared_memory_entries) { 23 if (!IsControllerActivated()) {
23 xpad_entry.header.timestamp = core_timing.GetCPUTicks(); 24 basic_xpad_lifo.buffer_count = 0;
24 xpad_entry.header.total_entry_count = 17; 25 basic_xpad_lifo.buffer_tail = 0;
25 26 std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
26 if (!IsControllerActivated()) { 27 return;
27 xpad_entry.header.entry_count = 0;
28 xpad_entry.header.last_entry_index = 0;
29 return;
30 }
31 xpad_entry.header.entry_count = 16;
32
33 const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
34 xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
35 auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
36
37 cur_entry.sampling_number = last_entry.sampling_number + 1;
38 cur_entry.sampling_number2 = cur_entry.sampling_number;
39 } 28 }
29
30 const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state;
31 next_state.sampling_number = last_entry.sampling_number + 1;
40 // TODO(ogniK): Update xpad states 32 // TODO(ogniK): Update xpad states
41 33
42 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 34 basic_xpad_lifo.WriteNextEntry(next_state);
35 std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
43} 36}
44 37
45void Controller_XPad::OnLoadInputDevices() {}
46} // namespace Service::HID 38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index f9ab5facf..ba8db8d9d 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -8,12 +8,14 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hid/hid_types.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14class Controller_XPad final : public ControllerBase { 16class Controller_XPad final : public ControllerBase {
15public: 17public:
16 explicit Controller_XPad(Core::System& system_); 18 explicit Controller_XPad(Core::HID::HIDCore& hid_core_);
17 ~Controller_XPad() override; 19 ~Controller_XPad() override;
18 20
19 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -25,13 +27,11 @@ public:
25 // When the controller is requesting an update for the shared memory 27 // When the controller is requesting an update for the shared memory
26 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
27 29
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private: 30private:
32 struct Attributes { 31 // This is nn::hid::BasicXpadAttributeSet
32 struct BasicXpadAttributeSet {
33 union { 33 union {
34 u32_le raw{}; 34 u32 raw{};
35 BitField<0, 1, u32> is_connected; 35 BitField<0, 1, u32> is_connected;
36 BitField<1, 1, u32> is_wired; 36 BitField<1, 1, u32> is_wired;
37 BitField<2, 1, u32> is_left_connected; 37 BitField<2, 1, u32> is_left_connected;
@@ -40,11 +40,12 @@ private:
40 BitField<5, 1, u32> is_right_wired; 40 BitField<5, 1, u32> is_right_wired;
41 }; 41 };
42 }; 42 };
43 static_assert(sizeof(Attributes) == 4, "Attributes is an invalid size"); 43 static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
44 44
45 struct Buttons { 45 // This is nn::hid::BasicXpadButtonSet
46 struct BasicXpadButtonSet {
46 union { 47 union {
47 u32_le raw{}; 48 u32 raw{};
48 // Button states 49 // Button states
49 BitField<0, 1, u32> a; 50 BitField<0, 1, u32> a;
50 BitField<1, 1, u32> b; 51 BitField<1, 1, u32> b;
@@ -88,35 +89,21 @@ private:
88 BitField<30, 1, u32> handheld_left_b; 89 BitField<30, 1, u32> handheld_left_b;
89 }; 90 };
90 }; 91 };
91 static_assert(sizeof(Buttons) == 4, "Buttons is an invalid size"); 92 static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
92 93
93 struct AnalogStick { 94 // This is nn::hid::detail::BasicXpadState
94 s32_le x; 95 struct BasicXpadState {
95 s32_le y; 96 s64 sampling_number;
96 }; 97 BasicXpadAttributeSet attributes;
97 static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size"); 98 BasicXpadButtonSet pad_states;
98 99 Core::HID::AnalogStickState l_stick;
99 struct XPadState { 100 Core::HID::AnalogStickState r_stick;
100 s64_le sampling_number;
101 s64_le sampling_number2;
102 Attributes attributes;
103 Buttons pad_states;
104 AnalogStick l_stick;
105 AnalogStick r_stick;
106 }; 101 };
107 static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size"); 102 static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
108 103
109 struct XPadEntry { 104 // This is nn::hid::detail::BasicXpadLifo
110 CommonHeader header; 105 Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
111 std::array<XPadState, 17> pad_states{}; 106 static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
112 INSERT_PADDING_BYTES(0x138); 107 BasicXpadState next_state{};
113 };
114 static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
115
116 struct SharedMemory {
117 std::array<XPadEntry, 4> shared_memory_entries{};
118 };
119 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
120 SharedMemory shared_memory{};
121}; 108};
122} // namespace Service::HID 109} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 10c64d41a..7163e1a4e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -8,7 +8,7 @@
8#include "common/settings.h" 8#include "common/settings.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/core_timing.h" 10#include "core/core_timing.h"
11#include "core/frontend/input.h" 11#include "core/hid/hid_core.h"
12#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_shared_memory.h" 14#include "core/hle/kernel/k_shared_memory.h"
@@ -34,10 +34,10 @@
34namespace Service::HID { 34namespace Service::HID {
35 35
36// Updating period for each HID device. 36// Updating period for each HID device.
37// HID is polled every 15ms, this value was derived from 37// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
38// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet 38constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
39constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz) 39constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
40constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz) 40constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
42 42
43IAppletResource::IAppletResource(Core::System& system_, 43IAppletResource::IAppletResource(Core::System& system_,
@@ -79,17 +79,24 @@ IAppletResource::IAppletResource(Core::System& system_,
79 const auto guard = LockService(); 79 const auto guard = LockService();
80 UpdateControllers(user_data, ns_late); 80 UpdateControllers(user_data, ns_late);
81 }); 81 });
82 mouse_keyboard_update_event = Core::Timing::CreateEvent(
83 "HID::UpdateMouseKeyboardCallback",
84 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
85 const auto guard = LockService();
86 UpdateMouseKeyboard(user_data, ns_late);
87 });
82 motion_update_event = Core::Timing::CreateEvent( 88 motion_update_event = Core::Timing::CreateEvent(
83 "HID::MotionPadCallback", 89 "HID::UpdateMotionCallback",
84 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 90 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
85 const auto guard = LockService(); 91 const auto guard = LockService();
86 UpdateMotion(user_data, ns_late); 92 UpdateMotion(user_data, ns_late);
87 }); 93 });
88 94
89 system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); 95 system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
96 system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event);
90 system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); 97 system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
91 98
92 ReloadInputDevices(); 99 system.HIDCore().ReloadInputDevices();
93} 100}
94 101
95void IAppletResource::ActivateController(HidController controller) { 102void IAppletResource::ActivateController(HidController controller) {
@@ -102,6 +109,7 @@ void IAppletResource::DeactivateController(HidController controller) {
102 109
103IAppletResource::~IAppletResource() { 110IAppletResource::~IAppletResource() {
104 system.CoreTiming().UnscheduleEvent(pad_update_event, 0); 111 system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
112 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
105 system.CoreTiming().UnscheduleEvent(motion_update_event, 0); 113 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
106} 114}
107 115
@@ -117,23 +125,44 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
117 std::chrono::nanoseconds ns_late) { 125 std::chrono::nanoseconds ns_late) {
118 auto& core_timing = system.CoreTiming(); 126 auto& core_timing = system.CoreTiming();
119 127
120 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
121 for (const auto& controller : controllers) { 128 for (const auto& controller : controllers) {
122 if (should_reload) { 129 // Keyboard has it's own update event
123 controller->OnLoadInputDevices(); 130 if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
131 continue;
132 }
133 // Mouse has it's own update event
134 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
135 continue;
124 } 136 }
125 controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(), 137 controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(),
126 SHARED_MEMORY_SIZE); 138 SHARED_MEMORY_SIZE);
127 } 139 }
128 140
129 // If ns_late is higher than the update rate ignore the delay 141 // If ns_late is higher than the update rate ignore the delay
130 if (ns_late > motion_update_ns) { 142 if (ns_late > pad_update_ns) {
131 ns_late = {}; 143 ns_late = {};
132 } 144 }
133 145
134 core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); 146 core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
135} 147}
136 148
149void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
150 std::chrono::nanoseconds ns_late) {
151 auto& core_timing = system.CoreTiming();
152
153 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(
154 core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
155 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(
156 core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
157
158 // If ns_late is higher than the update rate ignore the delay
159 if (ns_late > mouse_keyboard_update_ns) {
160 ns_late = {};
161 }
162
163 core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
164}
165
137void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 166void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
138 auto& core_timing = system.CoreTiming(); 167 auto& core_timing = system.CoreTiming();
139 168
@@ -166,7 +195,7 @@ public:
166private: 195private:
167 void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) { 196 void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) {
168 IPC::RequestParser rp{ctx}; 197 IPC::RequestParser rp{ctx};
169 const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 198 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
170 199
171 if (applet_resource != nullptr) { 200 if (applet_resource != nullptr) {
172 applet_resource->GetController<Controller_NPad>(HidController::NPad) 201 applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -264,8 +293,8 @@ Hid::Hid(Core::System& system_)
264 {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, 293 {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
265 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, 294 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
266 {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, 295 {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
267 {135, nullptr, "SetNpadCaptureButtonAssignment"}, 296 {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
268 {136, nullptr, "ClearNpadCaptureButtonAssignment"}, 297 {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
269 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, 298 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
270 {201, &Hid::SendVibrationValue, "SendVibrationValue"}, 299 {201, &Hid::SendVibrationValue, "SendVibrationValue"},
271 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, 300 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -422,6 +451,7 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
422 INSERT_PADDING_WORDS_NOINIT(1); 451 INSERT_PADDING_WORDS_NOINIT(1);
423 u64 applet_resource_user_id; 452 u64 applet_resource_user_id;
424 }; 453 };
454 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
425 455
426 const auto parameters{rp.PopRaw<Parameters>()}; 456 const auto parameters{rp.PopRaw<Parameters>()};
427 457
@@ -448,19 +478,18 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
448void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { 478void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
449 IPC::RequestParser rp{ctx}; 479 IPC::RequestParser rp{ctx};
450 struct Parameters { 480 struct Parameters {
451 Controller_NPad::DeviceHandle sixaxis_handle; 481 u32 basic_xpad_id;
452 INSERT_PADDING_WORDS_NOINIT(1); 482 INSERT_PADDING_WORDS_NOINIT(1);
453 u64 applet_resource_user_id; 483 u64 applet_resource_user_id;
454 }; 484 };
485 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
455 486
456 const auto parameters{rp.PopRaw<Parameters>()}; 487 const auto parameters{rp.PopRaw<Parameters>()};
457 488
458 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); 489 // This function does nothing on 10.0.0+
459 490
460 LOG_DEBUG(Service_HID, 491 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
461 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 492 parameters.basic_xpad_id, parameters.applet_resource_user_id);
462 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
463 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
464 493
465 IPC::ResponseBuilder rb{ctx, 2}; 494 IPC::ResponseBuilder rb{ctx, 2};
466 rb.Push(ResultSuccess); 495 rb.Push(ResultSuccess);
@@ -469,19 +498,18 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
469void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { 498void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
470 IPC::RequestParser rp{ctx}; 499 IPC::RequestParser rp{ctx};
471 struct Parameters { 500 struct Parameters {
472 Controller_NPad::DeviceHandle sixaxis_handle; 501 u32 basic_xpad_id;
473 INSERT_PADDING_WORDS_NOINIT(1); 502 INSERT_PADDING_WORDS_NOINIT(1);
474 u64 applet_resource_user_id; 503 u64 applet_resource_user_id;
475 }; 504 };
505 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
476 506
477 const auto parameters{rp.PopRaw<Parameters>()}; 507 const auto parameters{rp.PopRaw<Parameters>()};
478 508
479 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); 509 // This function does nothing on 10.0.0+
480 510
481 LOG_DEBUG(Service_HID, 511 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
482 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 512 parameters.basic_xpad_id, parameters.applet_resource_user_id);
483 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
484 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
485 513
486 IPC::ResponseBuilder rb{ctx, 2}; 514 IPC::ResponseBuilder rb{ctx, 2};
487 rb.Push(ResultSuccess); 515 rb.Push(ResultSuccess);
@@ -490,14 +518,16 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
490void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 518void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
491 IPC::RequestParser rp{ctx}; 519 IPC::RequestParser rp{ctx};
492 struct Parameters { 520 struct Parameters {
493 Controller_NPad::DeviceHandle sixaxis_handle; 521 Core::HID::SixAxisSensorHandle sixaxis_handle;
494 INSERT_PADDING_WORDS_NOINIT(1); 522 INSERT_PADDING_WORDS_NOINIT(1);
495 u64 applet_resource_user_id; 523 u64 applet_resource_user_id;
496 }; 524 };
525 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
497 526
498 const auto parameters{rp.PopRaw<Parameters>()}; 527 const auto parameters{rp.PopRaw<Parameters>()};
499 528
500 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); 529 applet_resource->GetController<Controller_NPad>(HidController::NPad)
530 .SetSixAxisEnabled(parameters.sixaxis_handle, true);
501 531
502 LOG_DEBUG(Service_HID, 532 LOG_DEBUG(Service_HID,
503 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 533 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -511,14 +541,16 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
511void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { 541void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
512 IPC::RequestParser rp{ctx}; 542 IPC::RequestParser rp{ctx};
513 struct Parameters { 543 struct Parameters {
514 Controller_NPad::DeviceHandle sixaxis_handle; 544 Core::HID::SixAxisSensorHandle sixaxis_handle;
515 INSERT_PADDING_WORDS_NOINIT(1); 545 INSERT_PADDING_WORDS_NOINIT(1);
516 u64 applet_resource_user_id; 546 u64 applet_resource_user_id;
517 }; 547 };
548 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
518 549
519 const auto parameters{rp.PopRaw<Parameters>()}; 550 const auto parameters{rp.PopRaw<Parameters>()};
520 551
521 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); 552 applet_resource->GetController<Controller_NPad>(HidController::NPad)
553 .SetSixAxisEnabled(parameters.sixaxis_handle, false);
522 554
523 LOG_DEBUG(Service_HID, 555 LOG_DEBUG(Service_HID,
524 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 556 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -534,19 +566,23 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
534 struct Parameters { 566 struct Parameters {
535 bool enable_sixaxis_sensor_fusion; 567 bool enable_sixaxis_sensor_fusion;
536 INSERT_PADDING_BYTES_NOINIT(3); 568 INSERT_PADDING_BYTES_NOINIT(3);
537 Controller_NPad::DeviceHandle sixaxis_handle; 569 Core::HID::SixAxisSensorHandle sixaxis_handle;
538 u64 applet_resource_user_id; 570 u64 applet_resource_user_id;
539 }; 571 };
540 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 572 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
541 573
542 const auto parameters{rp.PopRaw<Parameters>()}; 574 const auto parameters{rp.PopRaw<Parameters>()};
543 575
544 LOG_WARNING(Service_HID, 576 applet_resource->GetController<Controller_NPad>(HidController::NPad)
545 "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " 577 .SetSixAxisFusionEnabled(parameters.sixaxis_handle,
546 "device_index={}, applet_resource_user_id={}", 578 parameters.enable_sixaxis_sensor_fusion);
547 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, 579
548 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, 580 LOG_DEBUG(Service_HID,
549 parameters.applet_resource_user_id); 581 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
582 "device_index={}, applet_resource_user_id={}",
583 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
584 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
585 parameters.applet_resource_user_id);
550 586
551 IPC::ResponseBuilder rb{ctx, 2}; 587 IPC::ResponseBuilder rb{ctx, 2};
552 rb.Push(ResultSuccess); 588 rb.Push(ResultSuccess);
@@ -555,9 +591,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
555void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 591void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
556 IPC::RequestParser rp{ctx}; 592 IPC::RequestParser rp{ctx};
557 struct Parameters { 593 struct Parameters {
558 Controller_NPad::DeviceHandle sixaxis_handle; 594 Core::HID::SixAxisSensorHandle sixaxis_handle;
559 f32 parameter1; 595 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
560 f32 parameter2; 596 INSERT_PADDING_WORDS_NOINIT(1);
561 u64 applet_resource_user_id; 597 u64 applet_resource_user_id;
562 }; 598 };
563 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 599 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
@@ -565,14 +601,14 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
565 const auto parameters{rp.PopRaw<Parameters>()}; 601 const auto parameters{rp.PopRaw<Parameters>()};
566 602
567 applet_resource->GetController<Controller_NPad>(HidController::NPad) 603 applet_resource->GetController<Controller_NPad>(HidController::NPad)
568 .SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2); 604 .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
569 605
570 LOG_WARNING(Service_HID, 606 LOG_DEBUG(Service_HID,
571 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " 607 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
572 "parameter2={}, applet_resource_user_id={}", 608 "parameter2={}, applet_resource_user_id={}",
573 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 609 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
574 parameters.sixaxis_handle.device_index, parameters.parameter1, 610 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
575 parameters.parameter2, parameters.applet_resource_user_id); 611 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
576 612
577 IPC::ResponseBuilder rb{ctx, 2}; 613 IPC::ResponseBuilder rb{ctx, 2};
578 rb.Push(ResultSuccess); 614 rb.Push(ResultSuccess);
@@ -581,35 +617,33 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
581void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 617void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
582 IPC::RequestParser rp{ctx}; 618 IPC::RequestParser rp{ctx};
583 struct Parameters { 619 struct Parameters {
584 Controller_NPad::DeviceHandle sixaxis_handle; 620 Core::HID::SixAxisSensorHandle sixaxis_handle;
621 INSERT_PADDING_WORDS_NOINIT(1);
585 u64 applet_resource_user_id; 622 u64 applet_resource_user_id;
586 }; 623 };
587 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 624 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
588 625
589 f32 parameter1 = 0;
590 f32 parameter2 = 0;
591 const auto parameters{rp.PopRaw<Parameters>()}; 626 const auto parameters{rp.PopRaw<Parameters>()};
592 627
593 std::tie(parameter1, parameter2) = 628 const auto sixaxis_fusion_parameters =
594 applet_resource->GetController<Controller_NPad>(HidController::NPad) 629 applet_resource->GetController<Controller_NPad>(HidController::NPad)
595 .GetSixAxisFusionParameters(); 630 .GetSixAxisFusionParameters(parameters.sixaxis_handle);
596 631
597 LOG_WARNING( 632 LOG_DEBUG(Service_HID,
598 Service_HID, 633 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
599 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 634 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
600 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 635 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
601 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
602 636
603 IPC::ResponseBuilder rb{ctx, 4}; 637 IPC::ResponseBuilder rb{ctx, 4};
604 rb.Push(ResultSuccess); 638 rb.Push(ResultSuccess);
605 rb.Push(parameter1); 639 rb.PushRaw(sixaxis_fusion_parameters);
606 rb.Push(parameter2);
607} 640}
608 641
609void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 642void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
610 IPC::RequestParser rp{ctx}; 643 IPC::RequestParser rp{ctx};
611 struct Parameters { 644 struct Parameters {
612 Controller_NPad::DeviceHandle sixaxis_handle; 645 Core::HID::SixAxisSensorHandle sixaxis_handle;
646 INSERT_PADDING_WORDS_NOINIT(1);
613 u64 applet_resource_user_id; 647 u64 applet_resource_user_id;
614 }; 648 };
615 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 649 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -617,13 +651,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
617 const auto parameters{rp.PopRaw<Parameters>()}; 651 const auto parameters{rp.PopRaw<Parameters>()};
618 652
619 applet_resource->GetController<Controller_NPad>(HidController::NPad) 653 applet_resource->GetController<Controller_NPad>(HidController::NPad)
620 .ResetSixAxisFusionParameters(); 654 .ResetSixAxisFusionParameters(parameters.sixaxis_handle);
621 655
622 LOG_WARNING( 656 LOG_DEBUG(Service_HID,
623 Service_HID, 657 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
624 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 658 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
625 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 659 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
626 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
627 660
628 IPC::ResponseBuilder rb{ctx, 2}; 661 IPC::ResponseBuilder rb{ctx, 2};
629 rb.Push(ResultSuccess); 662 rb.Push(ResultSuccess);
@@ -631,12 +664,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
631 664
632void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 665void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
633 IPC::RequestParser rp{ctx}; 666 IPC::RequestParser rp{ctx};
634 const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 667 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
635 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; 668 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
636 const auto applet_resource_user_id{rp.Pop<u64>()}; 669 const auto applet_resource_user_id{rp.Pop<u64>()};
637 670
638 applet_resource->GetController<Controller_NPad>(HidController::NPad) 671 applet_resource->GetController<Controller_NPad>(HidController::NPad)
639 .SetGyroscopeZeroDriftMode(drift_mode); 672 .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
640 673
641 LOG_DEBUG(Service_HID, 674 LOG_DEBUG(Service_HID,
642 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " 675 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -651,10 +684,11 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
651void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 684void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
652 IPC::RequestParser rp{ctx}; 685 IPC::RequestParser rp{ctx};
653 struct Parameters { 686 struct Parameters {
654 Controller_NPad::DeviceHandle sixaxis_handle; 687 Core::HID::SixAxisSensorHandle sixaxis_handle;
655 INSERT_PADDING_WORDS_NOINIT(1); 688 INSERT_PADDING_WORDS_NOINIT(1);
656 u64 applet_resource_user_id; 689 u64 applet_resource_user_id;
657 }; 690 };
691 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
658 692
659 const auto parameters{rp.PopRaw<Parameters>()}; 693 const auto parameters{rp.PopRaw<Parameters>()};
660 694
@@ -666,21 +700,23 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
666 IPC::ResponseBuilder rb{ctx, 3}; 700 IPC::ResponseBuilder rb{ctx, 3};
667 rb.Push(ResultSuccess); 701 rb.Push(ResultSuccess);
668 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) 702 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
669 .GetGyroscopeZeroDriftMode()); 703 .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
670} 704}
671 705
672void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 706void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
673 IPC::RequestParser rp{ctx}; 707 IPC::RequestParser rp{ctx};
674 struct Parameters { 708 struct Parameters {
675 Controller_NPad::DeviceHandle sixaxis_handle; 709 Core::HID::SixAxisSensorHandle sixaxis_handle;
676 INSERT_PADDING_WORDS_NOINIT(1); 710 INSERT_PADDING_WORDS_NOINIT(1);
677 u64 applet_resource_user_id; 711 u64 applet_resource_user_id;
678 }; 712 };
713 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
679 714
680 const auto parameters{rp.PopRaw<Parameters>()}; 715 const auto parameters{rp.PopRaw<Parameters>()};
716 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
681 717
682 applet_resource->GetController<Controller_NPad>(HidController::NPad) 718 applet_resource->GetController<Controller_NPad>(HidController::NPad)
683 .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); 719 .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
684 720
685 LOG_DEBUG(Service_HID, 721 LOG_DEBUG(Service_HID,
686 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 722 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -694,10 +730,11 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
694void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { 730void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
695 IPC::RequestParser rp{ctx}; 731 IPC::RequestParser rp{ctx};
696 struct Parameters { 732 struct Parameters {
697 Controller_NPad::DeviceHandle sixaxis_handle; 733 Core::HID::SixAxisSensorHandle sixaxis_handle;
698 INSERT_PADDING_WORDS_NOINIT(1); 734 INSERT_PADDING_WORDS_NOINIT(1);
699 u64 applet_resource_user_id; 735 u64 applet_resource_user_id;
700 }; 736 };
737 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
701 738
702 const auto parameters{rp.PopRaw<Parameters>()}; 739 const auto parameters{rp.PopRaw<Parameters>()};
703 740
@@ -709,16 +746,17 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
709 IPC::ResponseBuilder rb{ctx, 3}; 746 IPC::ResponseBuilder rb{ctx, 3};
710 rb.Push(ResultSuccess); 747 rb.Push(ResultSuccess);
711 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 748 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
712 .IsSixAxisSensorAtRest()); 749 .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
713} 750}
714 751
715void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { 752void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
716 IPC::RequestParser rp{ctx}; 753 IPC::RequestParser rp{ctx};
717 struct Parameters { 754 struct Parameters {
718 Controller_NPad::DeviceHandle sixaxis_handle; 755 Core::HID::SixAxisSensorHandle sixaxis_handle;
719 INSERT_PADDING_WORDS_NOINIT(1); 756 INSERT_PADDING_WORDS_NOINIT(1);
720 u64 applet_resource_user_id; 757 u64 applet_resource_user_id;
721 }; 758 };
759 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
722 760
723 const auto parameters{rp.PopRaw<Parameters>()}; 761 const auto parameters{rp.PopRaw<Parameters>()};
724 762
@@ -740,13 +778,14 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
740 INSERT_PADDING_WORDS_NOINIT(1); 778 INSERT_PADDING_WORDS_NOINIT(1);
741 u64 applet_resource_user_id; 779 u64 applet_resource_user_id;
742 }; 780 };
781 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
743 782
744 const auto parameters{rp.PopRaw<Parameters>()}; 783 const auto parameters{rp.PopRaw<Parameters>()};
745 784
746 applet_resource->ActivateController(HidController::Gesture); 785 applet_resource->ActivateController(HidController::Gesture);
747 786
748 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, 787 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
749 parameters.applet_resource_user_id); 788 parameters.unknown, parameters.applet_resource_user_id);
750 789
751 IPC::ResponseBuilder rb{ctx, 2}; 790 IPC::ResponseBuilder rb{ctx, 2};
752 rb.Push(ResultSuccess); 791 rb.Push(ResultSuccess);
@@ -754,12 +793,20 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
754 793
755void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 794void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
756 IPC::RequestParser rp{ctx}; 795 IPC::RequestParser rp{ctx};
757 const auto supported_styleset{rp.Pop<u32>()}; 796 struct Parameters {
797 Core::HID::NpadStyleSet supported_styleset;
798 INSERT_PADDING_WORDS_NOINIT(1);
799 u64 applet_resource_user_id;
800 };
801 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
802
803 const auto parameters{rp.PopRaw<Parameters>()};
758 804
759 applet_resource->GetController<Controller_NPad>(HidController::NPad) 805 applet_resource->GetController<Controller_NPad>(HidController::NPad)
760 .SetSupportedStyleSet({supported_styleset}); 806 .SetSupportedStyleSet({parameters.supported_styleset});
761 807
762 LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); 808 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
809 parameters.supported_styleset, parameters.applet_resource_user_id);
763 810
764 IPC::ResponseBuilder rb{ctx, 2}; 811 IPC::ResponseBuilder rb{ctx, 2};
765 rb.Push(ResultSuccess); 812 rb.Push(ResultSuccess);
@@ -773,9 +820,9 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
773 820
774 IPC::ResponseBuilder rb{ctx, 3}; 821 IPC::ResponseBuilder rb{ctx, 3};
775 rb.Push(ResultSuccess); 822 rb.Push(ResultSuccess);
776 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 823 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
777 .GetSupportedStyleSet() 824 .GetSupportedStyleSet()
778 .raw); 825 .raw);
779} 826}
780 827
781void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 828void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
@@ -818,11 +865,12 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
818void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 865void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
819 IPC::RequestParser rp{ctx}; 866 IPC::RequestParser rp{ctx};
820 struct Parameters { 867 struct Parameters {
821 u32 npad_id; 868 Core::HID::NpadIdType npad_id;
822 INSERT_PADDING_WORDS_NOINIT(1); 869 INSERT_PADDING_WORDS_NOINIT(1);
823 u64 applet_resource_user_id; 870 u64 applet_resource_user_id;
824 u64 unknown; 871 u64 unknown;
825 }; 872 };
873 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
826 874
827 const auto parameters{rp.PopRaw<Parameters>()}; 875 const auto parameters{rp.PopRaw<Parameters>()};
828 876
@@ -838,10 +886,11 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
838void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { 886void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
839 IPC::RequestParser rp{ctx}; 887 IPC::RequestParser rp{ctx};
840 struct Parameters { 888 struct Parameters {
841 u32 npad_id; 889 Core::HID::NpadIdType npad_id;
842 INSERT_PADDING_WORDS_NOINIT(1); 890 INSERT_PADDING_WORDS_NOINIT(1);
843 u64 applet_resource_user_id; 891 u64 applet_resource_user_id;
844 }; 892 };
893 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
845 894
846 const auto parameters{rp.PopRaw<Parameters>()}; 895 const auto parameters{rp.PopRaw<Parameters>()};
847 896
@@ -857,7 +906,7 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
857 906
858void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 907void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
859 IPC::RequestParser rp{ctx}; 908 IPC::RequestParser rp{ctx};
860 const auto npad_id{rp.Pop<u32>()}; 909 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
861 910
862 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); 911 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
863 912
@@ -872,16 +921,17 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
872 // Should have no effect with how our npad sets up the data 921 // Should have no effect with how our npad sets up the data
873 IPC::RequestParser rp{ctx}; 922 IPC::RequestParser rp{ctx};
874 struct Parameters { 923 struct Parameters {
875 u32 unknown; 924 s32 revision;
876 INSERT_PADDING_WORDS_NOINIT(1); 925 INSERT_PADDING_WORDS_NOINIT(1);
877 u64 applet_resource_user_id; 926 u64 applet_resource_user_id;
878 }; 927 };
928 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
879 929
880 const auto parameters{rp.PopRaw<Parameters>()}; 930 const auto parameters{rp.PopRaw<Parameters>()};
881 931
882 applet_resource->ActivateController(HidController::NPad); 932 applet_resource->ActivateController(HidController::NPad);
883 933
884 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, 934 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
885 parameters.applet_resource_user_id); 935 parameters.applet_resource_user_id);
886 936
887 IPC::ResponseBuilder rb{ctx, 2}; 937 IPC::ResponseBuilder rb{ctx, 2};
@@ -891,7 +941,7 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
891void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 941void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
892 IPC::RequestParser rp{ctx}; 942 IPC::RequestParser rp{ctx};
893 const auto applet_resource_user_id{rp.Pop<u64>()}; 943 const auto applet_resource_user_id{rp.Pop<u64>()};
894 const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()}; 944 const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
895 945
896 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type); 946 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
897 947
@@ -916,42 +966,44 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
916void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 966void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
917 IPC::RequestParser rp{ctx}; 967 IPC::RequestParser rp{ctx};
918 struct Parameters { 968 struct Parameters {
919 u32 npad_id; 969 Core::HID::NpadIdType npad_id;
920 INSERT_PADDING_WORDS_NOINIT(1); 970 INSERT_PADDING_WORDS_NOINIT(1);
921 u64 applet_resource_user_id; 971 u64 applet_resource_user_id;
922 }; 972 };
973 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
923 974
924 const auto parameters{rp.PopRaw<Parameters>()}; 975 const auto parameters{rp.PopRaw<Parameters>()};
925 976
926 applet_resource->GetController<Controller_NPad>(HidController::NPad) 977 applet_resource->GetController<Controller_NPad>(HidController::NPad)
927 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); 978 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
979 Controller_NPad::NpadJoyAssignmentMode::Single);
928 980
929 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 981 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
930 parameters.npad_id, parameters.applet_resource_user_id); 982 parameters.applet_resource_user_id);
931 983
932 IPC::ResponseBuilder rb{ctx, 2}; 984 IPC::ResponseBuilder rb{ctx, 2};
933 rb.Push(ResultSuccess); 985 rb.Push(ResultSuccess);
934} 986}
935 987
936void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { 988void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
937 // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
938 IPC::RequestParser rp{ctx}; 989 IPC::RequestParser rp{ctx};
939 struct Parameters { 990 struct Parameters {
940 u32 npad_id; 991 Core::HID::NpadIdType npad_id;
941 INSERT_PADDING_WORDS_NOINIT(1); 992 INSERT_PADDING_WORDS_NOINIT(1);
942 u64 applet_resource_user_id; 993 u64 applet_resource_user_id;
943 u64 npad_joy_device_type; 994 Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
944 }; 995 };
996 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
945 997
946 const auto parameters{rp.PopRaw<Parameters>()}; 998 const auto parameters{rp.PopRaw<Parameters>()};
947 999
948 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1000 applet_resource->GetController<Controller_NPad>(HidController::NPad)
949 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); 1001 .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
1002 Controller_NPad::NpadJoyAssignmentMode::Single);
950 1003
951 LOG_WARNING(Service_HID, 1004 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
952 "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1005 parameters.npad_id, parameters.applet_resource_user_id,
953 parameters.npad_id, parameters.applet_resource_user_id, 1006 parameters.npad_joy_device_type);
954 parameters.npad_joy_device_type);
955 1007
956 IPC::ResponseBuilder rb{ctx, 2}; 1008 IPC::ResponseBuilder rb{ctx, 2};
957 rb.Push(ResultSuccess); 1009 rb.Push(ResultSuccess);
@@ -960,18 +1012,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
960void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 1012void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
961 IPC::RequestParser rp{ctx}; 1013 IPC::RequestParser rp{ctx};
962 struct Parameters { 1014 struct Parameters {
963 u32 npad_id; 1015 Core::HID::NpadIdType npad_id;
964 INSERT_PADDING_WORDS_NOINIT(1); 1016 INSERT_PADDING_WORDS_NOINIT(1);
965 u64 applet_resource_user_id; 1017 u64 applet_resource_user_id;
966 }; 1018 };
1019 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
967 1020
968 const auto parameters{rp.PopRaw<Parameters>()}; 1021 const auto parameters{rp.PopRaw<Parameters>()};
969 1022
970 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1023 applet_resource->GetController<Controller_NPad>(HidController::NPad)
971 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual); 1024 .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
972 1025
973 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 1026 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
974 parameters.npad_id, parameters.applet_resource_user_id); 1027 parameters.applet_resource_user_id);
975 1028
976 IPC::ResponseBuilder rb{ctx, 2}; 1029 IPC::ResponseBuilder rb{ctx, 2};
977 rb.Push(ResultSuccess); 1030 rb.Push(ResultSuccess);
@@ -979,8 +1032,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
979 1032
980void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 1033void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
981 IPC::RequestParser rp{ctx}; 1034 IPC::RequestParser rp{ctx};
982 const auto npad_id_1{rp.Pop<u32>()}; 1035 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
983 const auto npad_id_2{rp.Pop<u32>()}; 1036 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
984 const auto applet_resource_user_id{rp.Pop<u64>()}; 1037 const auto applet_resource_user_id{rp.Pop<u64>()};
985 1038
986 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1039 applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1046,8 +1099,8 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
1046 1099
1047void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { 1100void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1048 IPC::RequestParser rp{ctx}; 1101 IPC::RequestParser rp{ctx};
1049 const auto npad_id_1{rp.Pop<u32>()}; 1102 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1050 const auto npad_id_2{rp.Pop<u32>()}; 1103 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1051 const auto applet_resource_user_id{rp.Pop<u64>()}; 1104 const auto applet_resource_user_id{rp.Pop<u64>()};
1052 1105
1053 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) 1106 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1068,10 +1121,11 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1068void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { 1121void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
1069 IPC::RequestParser rp{ctx}; 1122 IPC::RequestParser rp{ctx};
1070 struct Parameters { 1123 struct Parameters {
1071 u32 npad_id; 1124 Core::HID::NpadIdType npad_id;
1072 INSERT_PADDING_WORDS_NOINIT(1); 1125 INSERT_PADDING_WORDS_NOINIT(1);
1073 u64 applet_resource_user_id; 1126 u64 applet_resource_user_id;
1074 }; 1127 };
1128 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1075 1129
1076 const auto parameters{rp.PopRaw<Parameters>()}; 1130 const auto parameters{rp.PopRaw<Parameters>()};
1077 1131
@@ -1089,9 +1143,10 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
1089 struct Parameters { 1143 struct Parameters {
1090 bool unintended_home_button_input_protection; 1144 bool unintended_home_button_input_protection;
1091 INSERT_PADDING_BYTES_NOINIT(3); 1145 INSERT_PADDING_BYTES_NOINIT(3);
1092 u32 npad_id; 1146 Core::HID::NpadIdType npad_id;
1093 u64 applet_resource_user_id; 1147 u64 applet_resource_user_id;
1094 }; 1148 };
1149 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1095 1150
1096 const auto parameters{rp.PopRaw<Parameters>()}; 1151 const auto parameters{rp.PopRaw<Parameters>()};
1097 1152
@@ -1113,6 +1168,7 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
1113 IPC::RequestParser rp{ctx}; 1168 IPC::RequestParser rp{ctx};
1114 struct Parameters { 1169 struct Parameters {
1115 bool analog_stick_use_center_clamp; 1170 bool analog_stick_use_center_clamp;
1171 INSERT_PADDING_BYTES_NOINIT(7);
1116 u64 applet_resource_user_id; 1172 u64 applet_resource_user_id;
1117 }; 1173 };
1118 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 1174 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -1130,40 +1186,71 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
1130 rb.Push(ResultSuccess); 1186 rb.Push(ResultSuccess);
1131} 1187}
1132 1188
1189void Hid::SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
1190 IPC::RequestParser rp{ctx};
1191 struct Parameters {
1192 Core::HID::NpadStyleSet npad_styleset;
1193 INSERT_PADDING_WORDS_NOINIT(1);
1194 u64 applet_resource_user_id;
1195 Core::HID::NpadButton button;
1196 };
1197 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1198
1199 const auto parameters{rp.PopRaw<Parameters>()};
1200
1201 LOG_WARNING(Service_HID,
1202 "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
1203 parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
1204
1205 IPC::ResponseBuilder rb{ctx, 2};
1206 rb.Push(ResultSuccess);
1207}
1208
1209void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
1210 IPC::RequestParser rp{ctx};
1211 const auto applet_resource_user_id{rp.Pop<u64>()};
1212
1213 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1214 applet_resource_user_id);
1215
1216 IPC::ResponseBuilder rb{ctx, 2};
1217 rb.Push(ResultSuccess);
1218}
1219
1133void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 1220void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
1134 IPC::RequestParser rp{ctx}; 1221 IPC::RequestParser rp{ctx};
1135 const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 1222 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1136 1223
1137 VibrationDeviceInfo vibration_device_info; 1224 Core::HID::VibrationDeviceInfo vibration_device_info;
1138 1225
1139 switch (vibration_device_handle.npad_type) { 1226 switch (vibration_device_handle.npad_type) {
1140 case Controller_NPad::NpadType::ProController: 1227 case Core::HID::NpadStyleIndex::ProController:
1141 case Controller_NPad::NpadType::Handheld: 1228 case Core::HID::NpadStyleIndex::Handheld:
1142 case Controller_NPad::NpadType::JoyconDual: 1229 case Core::HID::NpadStyleIndex::JoyconDual:
1143 case Controller_NPad::NpadType::JoyconLeft: 1230 case Core::HID::NpadStyleIndex::JoyconLeft:
1144 case Controller_NPad::NpadType::JoyconRight: 1231 case Core::HID::NpadStyleIndex::JoyconRight:
1145 default: 1232 default:
1146 vibration_device_info.type = VibrationDeviceType::LinearResonantActuator; 1233 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1147 break; 1234 break;
1148 case Controller_NPad::NpadType::GameCube: 1235 case Core::HID::NpadStyleIndex::GameCube:
1149 vibration_device_info.type = VibrationDeviceType::GcErm; 1236 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1150 break; 1237 break;
1151 case Controller_NPad::NpadType::Pokeball: 1238 case Core::HID::NpadStyleIndex::Pokeball:
1152 vibration_device_info.type = VibrationDeviceType::Unknown; 1239 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1153 break; 1240 break;
1154 } 1241 }
1155 1242
1156 switch (vibration_device_handle.device_index) { 1243 switch (vibration_device_handle.device_index) {
1157 case Controller_NPad::DeviceIndex::Left: 1244 case Core::HID::DeviceIndex::Left:
1158 vibration_device_info.position = VibrationDevicePosition::Left; 1245 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1159 break; 1246 break;
1160 case Controller_NPad::DeviceIndex::Right: 1247 case Core::HID::DeviceIndex::Right:
1161 vibration_device_info.position = VibrationDevicePosition::Right; 1248 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1162 break; 1249 break;
1163 case Controller_NPad::DeviceIndex::None: 1250 case Core::HID::DeviceIndex::None:
1164 default: 1251 default:
1165 UNREACHABLE_MSG("DeviceIndex should never be None!"); 1252 UNREACHABLE_MSG("DeviceIndex should never be None!");
1166 vibration_device_info.position = VibrationDevicePosition::None; 1253 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1167 break; 1254 break;
1168 } 1255 }
1169 1256
@@ -1178,11 +1265,12 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
1178void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { 1265void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
1179 IPC::RequestParser rp{ctx}; 1266 IPC::RequestParser rp{ctx};
1180 struct Parameters { 1267 struct Parameters {
1181 Controller_NPad::DeviceHandle vibration_device_handle; 1268 Core::HID::VibrationDeviceHandle vibration_device_handle;
1182 Controller_NPad::VibrationValue vibration_value; 1269 Core::HID::VibrationValue vibration_value;
1183 INSERT_PADDING_WORDS_NOINIT(1); 1270 INSERT_PADDING_WORDS_NOINIT(1);
1184 u64 applet_resource_user_id; 1271 u64 applet_resource_user_id;
1185 }; 1272 };
1273 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1186 1274
1187 const auto parameters{rp.PopRaw<Parameters>()}; 1275 const auto parameters{rp.PopRaw<Parameters>()};
1188 1276
@@ -1202,10 +1290,11 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
1202void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 1290void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
1203 IPC::RequestParser rp{ctx}; 1291 IPC::RequestParser rp{ctx};
1204 struct Parameters { 1292 struct Parameters {
1205 Controller_NPad::DeviceHandle vibration_device_handle; 1293 Core::HID::VibrationDeviceHandle vibration_device_handle;
1206 INSERT_PADDING_WORDS_NOINIT(1); 1294 INSERT_PADDING_WORDS_NOINIT(1);
1207 u64 applet_resource_user_id; 1295 u64 applet_resource_user_id;
1208 }; 1296 };
1297 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1209 1298
1210 const auto parameters{rp.PopRaw<Parameters>()}; 1299 const auto parameters{rp.PopRaw<Parameters>()};
1211 1300
@@ -1256,10 +1345,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
1256 const auto handles = ctx.ReadBuffer(0); 1345 const auto handles = ctx.ReadBuffer(0);
1257 const auto vibrations = ctx.ReadBuffer(1); 1346 const auto vibrations = ctx.ReadBuffer(1);
1258 1347
1259 std::vector<Controller_NPad::DeviceHandle> vibration_device_handles( 1348 std::vector<Core::HID::VibrationDeviceHandle> vibration_device_handles(
1260 handles.size() / sizeof(Controller_NPad::DeviceHandle)); 1349 handles.size() / sizeof(Core::HID::VibrationDeviceHandle));
1261 std::vector<Controller_NPad::VibrationValue> vibration_values( 1350 std::vector<Core::HID::VibrationValue> vibration_values(vibrations.size() /
1262 vibrations.size() / sizeof(Controller_NPad::VibrationValue)); 1351 sizeof(Core::HID::VibrationValue));
1263 1352
1264 std::memcpy(vibration_device_handles.data(), handles.data(), handles.size()); 1353 std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
1265 std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size()); 1354 std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
@@ -1276,9 +1365,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
1276void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { 1365void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1277 IPC::RequestParser rp{ctx}; 1366 IPC::RequestParser rp{ctx};
1278 struct Parameters { 1367 struct Parameters {
1279 Controller_NPad::DeviceHandle vibration_device_handle; 1368 Core::HID::VibrationDeviceHandle vibration_device_handle;
1369 INSERT_PADDING_WORDS_NOINIT(1);
1280 u64 applet_resource_user_id; 1370 u64 applet_resource_user_id;
1281 VibrationGcErmCommand gc_erm_command; 1371 Core::HID::VibrationGcErmCommand gc_erm_command;
1282 }; 1372 };
1283 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 1373 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1284 1374
@@ -1292,26 +1382,26 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1292 */ 1382 */
1293 const auto vibration_value = [parameters] { 1383 const auto vibration_value = [parameters] {
1294 switch (parameters.gc_erm_command) { 1384 switch (parameters.gc_erm_command) {
1295 case VibrationGcErmCommand::Stop: 1385 case Core::HID::VibrationGcErmCommand::Stop:
1296 return Controller_NPad::VibrationValue{ 1386 return Core::HID::VibrationValue{
1297 .amp_low = 0.0f, 1387 .low_amplitude = 0.0f,
1298 .freq_low = 160.0f, 1388 .low_frequency = 160.0f,
1299 .amp_high = 0.0f, 1389 .high_amplitude = 0.0f,
1300 .freq_high = 320.0f, 1390 .high_frequency = 320.0f,
1301 }; 1391 };
1302 case VibrationGcErmCommand::Start: 1392 case Core::HID::VibrationGcErmCommand::Start:
1303 return Controller_NPad::VibrationValue{ 1393 return Core::HID::VibrationValue{
1304 .amp_low = 1.0f, 1394 .low_amplitude = 1.0f,
1305 .freq_low = 160.0f, 1395 .low_frequency = 160.0f,
1306 .amp_high = 1.0f, 1396 .high_amplitude = 1.0f,
1307 .freq_high = 320.0f, 1397 .high_frequency = 320.0f,
1308 }; 1398 };
1309 case VibrationGcErmCommand::StopHard: 1399 case Core::HID::VibrationGcErmCommand::StopHard:
1310 return Controller_NPad::VibrationValue{ 1400 return Core::HID::VibrationValue{
1311 .amp_low = 0.0f, 1401 .low_amplitude = 0.0f,
1312 .freq_low = 0.0f, 1402 .low_frequency = 0.0f,
1313 .amp_high = 0.0f, 1403 .high_amplitude = 0.0f,
1314 .freq_high = 0.0f, 1404 .high_frequency = 0.0f,
1315 }; 1405 };
1316 default: 1406 default:
1317 return Controller_NPad::DEFAULT_VIBRATION_VALUE; 1407 return Controller_NPad::DEFAULT_VIBRATION_VALUE;
@@ -1336,7 +1426,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1336void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { 1426void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1337 IPC::RequestParser rp{ctx}; 1427 IPC::RequestParser rp{ctx};
1338 struct Parameters { 1428 struct Parameters {
1339 Controller_NPad::DeviceHandle vibration_device_handle; 1429 Core::HID::VibrationDeviceHandle vibration_device_handle;
1340 INSERT_PADDING_WORDS_NOINIT(1); 1430 INSERT_PADDING_WORDS_NOINIT(1);
1341 u64 applet_resource_user_id; 1431 u64 applet_resource_user_id;
1342 }; 1432 };
@@ -1347,8 +1437,8 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1347 .GetLastVibration(parameters.vibration_device_handle); 1437 .GetLastVibration(parameters.vibration_device_handle);
1348 1438
1349 const auto gc_erm_command = [last_vibration] { 1439 const auto gc_erm_command = [last_vibration] {
1350 if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) { 1440 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1351 return VibrationGcErmCommand::Start; 1441 return Core::HID::VibrationGcErmCommand::Start;
1352 } 1442 }
1353 1443
1354 /** 1444 /**
@@ -1357,11 +1447,11 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1357 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. 1447 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1358 * This is done to reuse the controller vibration functions made for regular controllers. 1448 * This is done to reuse the controller vibration functions made for regular controllers.
1359 */ 1449 */
1360 if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) { 1450 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1361 return VibrationGcErmCommand::StopHard; 1451 return Core::HID::VibrationGcErmCommand::StopHard;
1362 } 1452 }
1363 1453
1364 return VibrationGcErmCommand::Stop; 1454 return Core::HID::VibrationGcErmCommand::Stop;
1365 }(); 1455 }();
1366 1456
1367 LOG_DEBUG(Service_HID, 1457 LOG_DEBUG(Service_HID,
@@ -1401,10 +1491,11 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
1401void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { 1491void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
1402 IPC::RequestParser rp{ctx}; 1492 IPC::RequestParser rp{ctx};
1403 struct Parameters { 1493 struct Parameters {
1404 Controller_NPad::DeviceHandle vibration_device_handle; 1494 Core::HID::VibrationDeviceHandle vibration_device_handle;
1405 INSERT_PADDING_WORDS_NOINIT(1); 1495 INSERT_PADDING_WORDS_NOINIT(1);
1406 u64 applet_resource_user_id; 1496 u64 applet_resource_user_id;
1407 }; 1497 };
1498 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1408 1499
1409 const auto parameters{rp.PopRaw<Parameters>()}; 1500 const auto parameters{rp.PopRaw<Parameters>()};
1410 1501
@@ -1435,18 +1526,18 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1435void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1526void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1436 IPC::RequestParser rp{ctx}; 1527 IPC::RequestParser rp{ctx};
1437 struct Parameters { 1528 struct Parameters {
1438 Controller_NPad::DeviceHandle sixaxis_handle; 1529 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1439 INSERT_PADDING_WORDS_NOINIT(1); 1530 INSERT_PADDING_WORDS_NOINIT(1);
1440 u64 applet_resource_user_id; 1531 u64 applet_resource_user_id;
1441 }; 1532 };
1533 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1442 1534
1443 const auto parameters{rp.PopRaw<Parameters>()}; 1535 const auto parameters{rp.PopRaw<Parameters>()};
1444 1536
1445 LOG_WARNING( 1537 LOG_WARNING(Service_HID,
1446 Service_HID, 1538 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1447 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1539 parameters.console_sixaxis_handle.unknown_1,
1448 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 1540 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1449 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
1450 1541
1451 IPC::ResponseBuilder rb{ctx, 2}; 1542 IPC::ResponseBuilder rb{ctx, 2};
1452 rb.Push(ResultSuccess); 1543 rb.Push(ResultSuccess);
@@ -1455,18 +1546,18 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1455void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1546void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1456 IPC::RequestParser rp{ctx}; 1547 IPC::RequestParser rp{ctx};
1457 struct Parameters { 1548 struct Parameters {
1458 Controller_NPad::DeviceHandle sixaxis_handle; 1549 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1459 INSERT_PADDING_WORDS_NOINIT(1); 1550 INSERT_PADDING_WORDS_NOINIT(1);
1460 u64 applet_resource_user_id; 1551 u64 applet_resource_user_id;
1461 }; 1552 };
1553 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1462 1554
1463 const auto parameters{rp.PopRaw<Parameters>()}; 1555 const auto parameters{rp.PopRaw<Parameters>()};
1464 1556
1465 LOG_WARNING( 1557 LOG_WARNING(Service_HID,
1466 Service_HID, 1558 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1467 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1559 parameters.console_sixaxis_handle.unknown_1,
1468 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 1560 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1469 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
1470 1561
1471 IPC::ResponseBuilder rb{ctx, 2}; 1562 IPC::ResponseBuilder rb{ctx, 2};
1472 rb.Push(ResultSuccess); 1563 rb.Push(ResultSuccess);
@@ -1620,10 +1711,8 @@ void Hid::SetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
1620 1711
1621void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) { 1712void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
1622 IPC::RequestParser rp{ctx}; 1713 IPC::RequestParser rp{ctx};
1623 const auto applet_resource_user_id{rp.Pop<u64>()};
1624 1714
1625 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", 1715 LOG_WARNING(Service_HID, "(STUBBED) called");
1626 applet_resource_user_id);
1627 1716
1628 IPC::ResponseBuilder rb{ctx, 4}; 1717 IPC::ResponseBuilder rb{ctx, 4};
1629 rb.Push(ResultSuccess); 1718 rb.Push(ResultSuccess);
@@ -1825,7 +1914,7 @@ public:
1825 {317, nullptr, "GetNpadLeftRightInterfaceType"}, 1914 {317, nullptr, "GetNpadLeftRightInterfaceType"},
1826 {318, nullptr, "HasBattery"}, 1915 {318, nullptr, "HasBattery"},
1827 {319, nullptr, "HasLeftRightBattery"}, 1916 {319, nullptr, "HasLeftRightBattery"},
1828 {321, nullptr, "GetUniquePadsFromNpad"}, 1917 {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
1829 {322, nullptr, "GetIrSensorState"}, 1918 {322, nullptr, "GetIrSensorState"},
1830 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 1919 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
1831 {324, nullptr, "GetUniquePadButtonSet"}, 1920 {324, nullptr, "GetUniquePadButtonSet"},
@@ -1996,6 +2085,18 @@ private:
1996 IPC::ResponseBuilder rb{ctx, 2}; 2085 IPC::ResponseBuilder rb{ctx, 2};
1997 rb.Push(ResultSuccess); 2086 rb.Push(ResultSuccess);
1998 } 2087 }
2088
2089 void GetUniquePadsFromNpad(Kernel::HLERequestContext& ctx) {
2090 IPC::RequestParser rp{ctx};
2091 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
2092
2093 const s64 total_entries = 0;
2094 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
2095
2096 IPC::ResponseBuilder rb{ctx, 3};
2097 rb.Push(ResultSuccess);
2098 rb.Push(total_entries);
2099 }
1999}; 2100};
2000 2101
2001class HidTmp final : public ServiceFramework<HidTmp> { 2102class HidTmp final : public ServiceFramework<HidTmp> {
@@ -2037,10 +2138,6 @@ public:
2037 } 2138 }
2038}; 2139};
2039 2140
2040void ReloadInputDevices() {
2041 Settings::values.is_device_reload_pending.store(true);
2042}
2043
2044void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 2141void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
2045 std::make_shared<Hid>(system)->InstallAsService(service_manager); 2142 std::make_shared<Hid>(system)->InstallAsService(service_manager);
2046 std::make_shared<HidBus>(system)->InstallAsService(service_manager); 2143 std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index b1fe75e94..d290df161 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -60,21 +60,23 @@ public:
60private: 60private:
61 template <typename T> 61 template <typename T>
62 void MakeController(HidController controller) { 62 void MakeController(HidController controller) {
63 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); 63 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore());
64 } 64 }
65 template <typename T> 65 template <typename T>
66 void MakeControllerWithServiceContext(HidController controller) { 66 void MakeControllerWithServiceContext(HidController controller) {
67 controllers[static_cast<std::size_t>(controller)] = 67 controllers[static_cast<std::size_t>(controller)] =
68 std::make_unique<T>(system, service_context); 68 std::make_unique<T>(system.HIDCore(), service_context);
69 } 69 }
70 70
71 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 71 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
72 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 72 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
73 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
73 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 74 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
74 75
75 KernelHelpers::ServiceContext& service_context; 76 KernelHelpers::ServiceContext& service_context;
76 77
77 std::shared_ptr<Core::Timing::EventType> pad_update_event; 78 std::shared_ptr<Core::Timing::EventType> pad_update_event;
79 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
78 std::shared_ptr<Core::Timing::EventType> motion_update_event; 80 std::shared_ptr<Core::Timing::EventType> motion_update_event;
79 81
80 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> 82 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -134,6 +136,8 @@ private:
134 void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); 136 void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
135 void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); 137 void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
136 void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx); 138 void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx);
139 void SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
140 void ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
137 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); 141 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
138 void SendVibrationValue(Kernel::HLERequestContext& ctx); 142 void SendVibrationValue(Kernel::HLERequestContext& ctx);
139 void GetActualVibrationValue(Kernel::HLERequestContext& ctx); 143 void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
@@ -161,38 +165,11 @@ private:
161 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); 165 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
162 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); 166 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
163 167
164 enum class VibrationDeviceType : u32 {
165 Unknown = 0,
166 LinearResonantActuator = 1,
167 GcErm = 2,
168 };
169
170 enum class VibrationDevicePosition : u32 {
171 None = 0,
172 Left = 1,
173 Right = 2,
174 };
175
176 enum class VibrationGcErmCommand : u64 {
177 Stop = 0,
178 Start = 1,
179 StopHard = 2,
180 };
181
182 struct VibrationDeviceInfo {
183 VibrationDeviceType type{};
184 VibrationDevicePosition position{};
185 };
186 static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
187
188 std::shared_ptr<IAppletResource> applet_resource; 168 std::shared_ptr<IAppletResource> applet_resource;
189 169
190 KernelHelpers::ServiceContext service_context; 170 KernelHelpers::ServiceContext service_context;
191}; 171};
192 172
193/// Reload input devices. Used when input configuration changed
194void ReloadInputDevices();
195
196/// Registers all HID services with the specified service manager. 173/// Registers all HID services with the specified service manager.
197void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 174void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
198 175
diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h
new file mode 100644
index 000000000..44c20d967
--- /dev/null
+++ b/src/core/hle/service/hid/ring_lifo.h
@@ -0,0 +1,54 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10
11namespace Service::HID {
12
13template <typename State>
14struct AtomicStorage {
15 s64 sampling_number;
16 State state;
17};
18
19template <typename State, std::size_t max_buffer_size>
20struct Lifo {
21 s64 timestamp{};
22 s64 total_buffer_count = static_cast<s64>(max_buffer_size);
23 s64 buffer_tail{};
24 s64 buffer_count{};
25 std::array<AtomicStorage<State>, max_buffer_size> entries{};
26
27 const AtomicStorage<State>& ReadCurrentEntry() const {
28 return entries[buffer_tail];
29 }
30
31 const AtomicStorage<State>& ReadPreviousEntry() const {
32 return entries[GetPreviousEntryIndex()];
33 }
34
35 std::size_t GetPreviousEntryIndex() const {
36 return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count);
37 }
38
39 std::size_t GetNextEntryIndex() const {
40 return static_cast<size_t>((buffer_tail + 1) % total_buffer_count);
41 }
42
43 void WriteNextEntry(const State& new_state) {
44 if (buffer_count < total_buffer_count - 1) {
45 buffer_count++;
46 }
47 buffer_tail = GetNextEntryIndex();
48 const auto& previous_entry = ReadPreviousEntry();
49 entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1;
50 entries[buffer_tail].state = new_state;
51 }
52};
53
54} // namespace Service::HID
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 32eff3b2a..3782703d2 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -396,12 +396,12 @@ public:
396 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, 396 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
397 nro_header.segment_headers[DATA_INDEX].memory_size); 397 nro_header.segment_headers[DATA_INDEX].memory_size);
398 398
399 CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( 399 CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
400 text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute)); 400 text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute));
401 CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(ro_start, data_start - ro_start, 401 CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
402 Kernel::KMemoryPermission::Read)); 402 ro_start, data_start - ro_start, Kernel::KMemoryPermission::Read));
403 403
404 return process->PageTable().SetCodeMemoryPermission( 404 return process->PageTable().SetProcessMemoryPermission(
405 data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite); 405 data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite);
406 } 406 }
407 407
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 64ffc8572..382ddcae5 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -12,6 +12,7 @@
12#include "core/hle/service/ns/errors.h" 12#include "core/hle/service/ns/errors.h"
13#include "core/hle/service/ns/language.h" 13#include "core/hle/service/ns/language.h"
14#include "core/hle/service/ns/ns.h" 14#include "core/hle/service/ns/ns.h"
15#include "core/hle/service/ns/pdm_qry.h"
15#include "core/hle/service/ns/pl_u.h" 16#include "core/hle/service/ns/pl_u.h"
16#include "core/hle/service/set/set.h" 17#include "core/hle/service/set/set.h"
17 18
@@ -570,11 +571,29 @@ IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
570 571
571IFactoryResetInterface::~IFactoryResetInterface() = default; 572IFactoryResetInterface::~IFactoryResetInterface() = default;
572 573
574IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
575 Core::System& system_)
576 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
577 // clang-format off
578 static const FunctionInfo functions[] = {
579 {0, nullptr, "GetApplicationControlData"},
580 {1, nullptr, "GetApplicationDesiredLanguage"},
581 {2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
582 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
583 {4, nullptr, "SelectApplicationDesiredLanguage"},
584 };
585 // clang-format on
586
587 RegisterHandlers(functions);
588}
589
590IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
591
573NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { 592NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
574 // clang-format off 593 // clang-format off
575 static const FunctionInfo functions[] = { 594 static const FunctionInfo functions[] = {
576 {7988, nullptr, "GetDynamicRightsInterface"}, 595 {7988, nullptr, "GetDynamicRightsInterface"},
577 {7989, nullptr, "GetReadOnlyApplicationControlDataInterface"}, 596 {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
578 {7991, nullptr, "GetReadOnlyApplicationRecordInterface"}, 597 {7991, nullptr, "GetReadOnlyApplicationRecordInterface"},
579 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, 598 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
580 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, 599 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
@@ -738,6 +757,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
738 std::make_shared<NS_SU>(system)->InstallAsService(service_manager); 757 std::make_shared<NS_SU>(system)->InstallAsService(service_manager);
739 std::make_shared<NS_VM>(system)->InstallAsService(service_manager); 758 std::make_shared<NS_VM>(system)->InstallAsService(service_manager);
740 759
760 std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager);
761
741 std::make_shared<PL_U>(system)->InstallAsService(service_manager); 762 std::make_shared<PL_U>(system)->InstallAsService(service_manager);
742} 763}
743 764
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 218eec3ec..43540b0fb 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -74,6 +74,13 @@ public:
74 ~IFactoryResetInterface() override; 74 ~IFactoryResetInterface() override;
75}; 75};
76 76
77class IReadOnlyApplicationControlDataInterface final
78 : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
79public:
80 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
81 ~IReadOnlyApplicationControlDataInterface() override;
82};
83
77class NS final : public ServiceFramework<NS> { 84class NS final : public ServiceFramework<NS> {
78public: 85public:
79 explicit NS(const char* name, Core::System& system_); 86 explicit NS(const char* name, Core::System& system_);
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
new file mode 100644
index 000000000..e2fab5c3f
--- /dev/null
+++ b/src/core/hle/service/ns/pdm_qry.cpp
@@ -0,0 +1,69 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "common/uuid.h"
9#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/ns/pdm_qry.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::NS {
15
16PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
17 // clang-format off
18 static const FunctionInfo functions[] = {
19 {0, nullptr, "QueryAppletEvent"},
20 {1, nullptr, "QueryPlayStatistics"},
21 {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
22 {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
23 {4, nullptr, "QueryPlayStatisticsByApplicationId"},
24 {5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
25 {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
26 {7, nullptr, "QueryLastPlayTimeV0"},
27 {8, nullptr, "QueryPlayEvent"},
28 {9, nullptr, "GetAvailablePlayEventRange"},
29 {10, nullptr, "QueryAccountEvent"},
30 {11, nullptr, "QueryAccountPlayEvent"},
31 {12, nullptr, "GetAvailableAccountPlayEventRange"},
32 {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
33 {14, nullptr, "QueryRecentlyPlayedApplication"},
34 {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
35 {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
36 {17, nullptr, "QueryLastPlayTime"},
37 {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
38 {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
39 };
40 // clang-format on
41
42 RegisterHandlers(functions);
43}
44
45PDM_QRY::~PDM_QRY() = default;
46
47void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx) {
48 IPC::RequestParser rp{ctx};
49 const auto unknown = rp.Pop<bool>();
50 rp.Pop<u8>(); // Padding
51 const auto application_id = rp.Pop<u64>();
52 const auto user_account_uid = rp.PopRaw<Common::UUID>();
53
54 // TODO(German77): Read statistics of the game
55 PlayStatistics statistics{
56 .application_id = application_id,
57 .total_launches = 1,
58 };
59
60 LOG_WARNING(Service_NS,
61 "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
62 unknown, application_id, user_account_uid.Format());
63
64 IPC::ResponseBuilder rb{ctx, 12};
65 rb.Push(ResultSuccess);
66 rb.PushRaw(statistics);
67}
68
69} // namespace Service::NS
diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h
new file mode 100644
index 000000000..516136314
--- /dev/null
+++ b/src/core/hle/service/ns/pdm_qry.h
@@ -0,0 +1,33 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service::NS {
10
11struct PlayStatistics {
12 u64 application_id{};
13 u32 first_entry_index{};
14 u32 first_timestamp_user{};
15 u32 first_timestamp_network{};
16 u32 last_entry_index{};
17 u32 last_timestamp_user{};
18 u32 last_timestamp_network{};
19 u32 play_time_in_minutes{};
20 u32 total_launches{};
21};
22static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
23
24class PDM_QRY final : public ServiceFramework<PDM_QRY> {
25public:
26 explicit PDM_QRY(Core::System& system_);
27 ~PDM_QRY() override;
28
29private:
30 void QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx);
31};
32
33} // namespace Service::NS
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 0d7d4ad03..8314d1ec2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -20,8 +20,12 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
23 case 0x1: 23 case 0x1: {
24 return Submit(input, output); 24 if (!fd_to_id.contains(fd)) {
25 fd_to_id[fd] = next_id++;
26 }
27 return Submit(fd, input, output);
28 }
25 case 0x2: 29 case 0x2:
26 return GetSyncpoint(input, output); 30 return GetSyncpoint(input, output);
27 case 0x3: 31 case 0x3:
@@ -66,7 +70,10 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {}
66 70
67void nvhost_nvdec::OnClose(DeviceFD fd) { 71void nvhost_nvdec::OnClose(DeviceFD fd) {
68 LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); 72 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
69 system.GPU().ClearCdmaInstance(); 73 const auto iter = fd_to_id.find(fd);
74 if (iter != fd_to_id.end()) {
75 system.GPU().ClearCdmaInstance(iter->second);
76 }
70} 77}
71 78
72} // namespace Service::Nvidia::Devices 79} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 523d96e3a..a507c4d0a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -24,6 +24,9 @@ public:
24 24
25 void OnOpen(DeviceFD fd) override; 25 void OnOpen(DeviceFD fd) override;
26 void OnClose(DeviceFD fd) override; 26 void OnClose(DeviceFD fd) override;
27
28private:
29 u32 next_id{};
27}; 30};
28 31
29} // namespace Service::Nvidia::Devices 32} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index e61261f98..8a05f0668 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -59,7 +59,8 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
59 return NvResult::Success; 59 return NvResult::Success;
60} 60}
61 61
62NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) { 62NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
63 std::vector<u8>& output) {
63 IoctlSubmit params{}; 64 IoctlSubmit params{};
64 std::memcpy(&params, input.data(), sizeof(IoctlSubmit)); 65 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
65 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); 66 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -93,7 +94,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
93 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); 94 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
94 system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), 95 system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
95 cmdlist.size() * sizeof(u32)); 96 cmdlist.size() * sizeof(u32));
96 gpu.PushCommandBuffer(cmdlist); 97 gpu.PushCommandBuffer(fd_to_id[fd], cmdlist);
97 } 98 }
98 std::memcpy(output.data(), &params, sizeof(IoctlSubmit)); 99 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
99 // Some games expect command_buffers to be written back 100 // Some games expect command_buffers to be written back
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 351625c17..e28c54df6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -104,13 +104,14 @@ protected:
104 104
105 /// Ioctl command implementations 105 /// Ioctl command implementations
106 NvResult SetNVMAPfd(const std::vector<u8>& input); 106 NvResult SetNVMAPfd(const std::vector<u8>& input);
107 NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output); 107 NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output);
108 NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); 108 NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
109 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 109 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
110 NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 110 NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
111 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 111 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
112 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 112 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
113 113
114 std::unordered_map<DeviceFD, u32> fd_to_id{};
114 s32_le nvmap_fd{}; 115 s32_le nvmap_fd{};
115 u32_le submit_timeout{}; 116 u32_le submit_timeout{};
116 std::shared_ptr<nvmap> nvmap_dev; 117 std::shared_ptr<nvmap> nvmap_dev;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index eac4dd530..76b39806f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -21,7 +21,10 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
23 case 0x1: 23 case 0x1:
24 return Submit(input, output); 24 if (!fd_to_id.contains(fd)) {
25 fd_to_id[fd] = next_id++;
26 }
27 return Submit(fd, input, output);
25 case 0x2: 28 case 0x2:
26 return GetSyncpoint(input, output); 29 return GetSyncpoint(input, output);
27 case 0x3: 30 case 0x3:
@@ -65,7 +68,10 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
65void nvhost_vic::OnOpen(DeviceFD fd) {} 68void nvhost_vic::OnOpen(DeviceFD fd) {}
66 69
67void nvhost_vic::OnClose(DeviceFD fd) { 70void nvhost_vic::OnClose(DeviceFD fd) {
68 system.GPU().ClearCdmaInstance(); 71 const auto iter = fd_to_id.find(fd);
72 if (iter != fd_to_id.end()) {
73 system.GPU().ClearCdmaInstance(iter->second);
74 }
69} 75}
70 76
71} // namespace Service::Nvidia::Devices 77} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 6d7fda9d1..c9732c037 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -23,5 +23,8 @@ public:
23 23
24 void OnOpen(DeviceFD fd) override; 24 void OnOpen(DeviceFD fd) override;
25 void OnClose(DeviceFD fd) override; 25 void OnClose(DeviceFD fd) override;
26
27private:
28 u32 next_id{};
26}; 29};
27} // namespace Service::Nvidia::Devices 30} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 3294bc0e7..5ab221fc1 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,3 +1,7 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#pragma once 5#pragma once
2 6
3#include <array> 7#include <array>
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 88fc5b5cc..277abc17a 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -13,7 +13,12 @@ namespace Service::PM {
13 13
14namespace { 14namespace {
15 15
16constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1}; 16constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1};
17[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2};
18[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3};
19[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4};
20[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5};
21[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6};
17 22
18constexpr u64 NO_PROCESS_FOUND_PID{0}; 23constexpr u64 NO_PROCESS_FOUND_PID{0};
19 24
@@ -95,18 +100,18 @@ public:
95private: 100private:
96 void GetProcessId(Kernel::HLERequestContext& ctx) { 101 void GetProcessId(Kernel::HLERequestContext& ctx) {
97 IPC::RequestParser rp{ctx}; 102 IPC::RequestParser rp{ctx};
98 const auto title_id = rp.PopRaw<u64>(); 103 const auto program_id = rp.PopRaw<u64>();
99 104
100 LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id); 105 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
101 106
102 const auto process = 107 const auto process =
103 SearchProcessList(kernel.GetProcessList(), [title_id](const auto& proc) { 108 SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) {
104 return proc->GetProgramID() == title_id; 109 return proc->GetProgramID() == program_id;
105 }); 110 });
106 111
107 if (!process.has_value()) { 112 if (!process.has_value()) {
108 IPC::ResponseBuilder rb{ctx, 2}; 113 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(ERROR_PROCESS_NOT_FOUND); 114 rb.Push(ResultProcessNotFound);
110 return; 115 return;
111 } 116 }
112 117
@@ -128,13 +133,16 @@ public:
128 explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_) 133 explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_)
129 : ServiceFramework{system_, "pm:info"}, process_list{process_list_} { 134 : ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
130 static const FunctionInfo functions[] = { 135 static const FunctionInfo functions[] = {
131 {0, &Info::GetTitleId, "GetTitleId"}, 136 {0, &Info::GetProgramId, "GetProgramId"},
137 {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"},
138 {65001, nullptr, "AtmosphereHasLaunchedProgram"},
139 {65002, nullptr, "AtmosphereGetProcessInfo"},
132 }; 140 };
133 RegisterHandlers(functions); 141 RegisterHandlers(functions);
134 } 142 }
135 143
136private: 144private:
137 void GetTitleId(Kernel::HLERequestContext& ctx) { 145 void GetProgramId(Kernel::HLERequestContext& ctx) {
138 IPC::RequestParser rp{ctx}; 146 IPC::RequestParser rp{ctx};
139 const auto process_id = rp.PopRaw<u64>(); 147 const auto process_id = rp.PopRaw<u64>();
140 148
@@ -146,7 +154,7 @@ private:
146 154
147 if (!process.has_value()) { 155 if (!process.has_value()) {
148 IPC::ResponseBuilder rb{ctx, 2}; 156 IPC::ResponseBuilder rb{ctx, 2};
149 rb.Push(ERROR_PROCESS_NOT_FOUND); 157 rb.Push(ResultProcessNotFound);
150 return; 158 return;
151 } 159 }
152 160
@@ -155,6 +163,27 @@ private:
155 rb.Push((*process)->GetProgramID()); 163 rb.Push((*process)->GetProgramID());
156 } 164 }
157 165
166 void AtmosphereGetProcessId(Kernel::HLERequestContext& ctx) {
167 IPC::RequestParser rp{ctx};
168 const auto program_id = rp.PopRaw<u64>();
169
170 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
171
172 const auto process = SearchProcessList(process_list, [program_id](const auto& proc) {
173 return proc->GetProgramID() == program_id;
174 });
175
176 if (!process.has_value()) {
177 IPC::ResponseBuilder rb{ctx, 2};
178 rb.Push(ResultProcessNotFound);
179 return;
180 }
181
182 IPC::ResponseBuilder rb{ctx, 4};
183 rb.Push(ResultSuccess);
184 rb.Push((*process)->GetProcessID());
185 }
186
158 const std::vector<Kernel::KProcess*>& process_list; 187 const std::vector<Kernel::KProcess*>& process_list;
159}; 188};
160 189
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 1b5aca65d..b47e3bf69 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -125,8 +125,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
125 } 125 }
126 metadata.Print(); 126 metadata.Print();
127 127
128 const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 128 const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
129 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}; 129 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
130 "subsdk8", "subsdk9", "sdk"};
130 131
131 // Use the NSO module loader to figure out the code layout 132 // Use the NSO module loader to figure out the code layout
132 std::size_t code_size{}; 133 std::size_t code_size{};
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 20f0e90f5..12446c9ac 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -19,7 +19,6 @@
19namespace Core::Memory { 19namespace Core::Memory {
20namespace { 20namespace {
21constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; 21constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
22constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
23 22
24std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) { 23std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) {
25 auto end_index = start_index; 24 auto end_index = start_index;
@@ -61,7 +60,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
61 applet_resource 60 applet_resource
62 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) 61 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
63 .GetAndResetPressState(); 62 .GetAndResetPressState();
64 return press_state & KEYPAD_BITMASK; 63 return static_cast<u64>(press_state & HID::NpadButton::All);
65} 64}
66 65
67void StandardVmCallbacks::DebugLog(u8 id, u64 value) { 66void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index a2541906f..816202588 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -33,7 +33,7 @@ public:
33 explicit PerfStats(u64 title_id_); 33 explicit PerfStats(u64 title_id_);
34 ~PerfStats(); 34 ~PerfStats();
35 35
36 using Clock = std::chrono::high_resolution_clock; 36 using Clock = std::chrono::steady_clock;
37 37
38 void BeginSystemFrame(); 38 void BeginSystemFrame();
39 void EndSystemFrame(); 39 void EndSystemFrame();
@@ -87,7 +87,7 @@ private:
87 87
88class SpeedLimiter { 88class SpeedLimiter {
89public: 89public:
90 using Clock = std::chrono::high_resolution_clock; 90 using Clock = std::chrono::steady_clock;
91 91
92 void DoSpeedLimiting(std::chrono::microseconds current_system_time_us); 92 void DoSpeedLimiting(std::chrono::microseconds current_system_time_us);
93 93