summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorFeng Chen <VonChenPlus@gmail.com>2021-12-18 06:57:14 +0100
committerGitHub <noreply@github.com>2021-12-18 06:57:14 +0100
commite49184e6069a9d791d2df3c1958f5c4b1187e124 (patch)
treeb776caf722e0be0e680f67b0ad0842628162ef1c /src/core
parentImplement convert legacy to generic (diff)
parentMerge pull request #7570 from ameerj/favorites-expanded (diff)
downloadyuzu-e49184e6069a9d791d2df3c1958f5c4b1187e124.tar
yuzu-e49184e6069a9d791d2df3c1958f5c4b1187e124.tar.gz
yuzu-e49184e6069a9d791d2df3c1958f5c4b1187e124.tar.bz2
yuzu-e49184e6069a9d791d2df3c1958f5c4b1187e124.tar.lz
yuzu-e49184e6069a9d791d2df3c1958f5c4b1187e124.tar.xz
yuzu-e49184e6069a9d791d2df3c1958f5c4b1187e124.tar.zst
yuzu-e49184e6069a9d791d2df3c1958f5c4b1187e124.zip
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
frontend/emu_window.h
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
- frontend/input_interpreter.cpp
- frontend/input_interpreter.h
- frontend/input.h
hardware_interrupt_manager.cpp
hardware_interrupt_manager.h
+ hid/emulated_console.cpp
+ hid/emulated_console.h
+ hid/emulated_controller.cpp
+ hid/emulated_controller.h
+ hid/emulated_devices.cpp
+ hid/emulated_devices.h
+ hid/hid_core.cpp
+ hid/hid_core.h
+ hid/hid_types.h
+ hid/input_converter.cpp
+ hid/input_converter.h
+ hid/input_interpreter.cpp
+ hid/input_interpreter.h
+ hid/motion_input.cpp
+ hid/motion_input.h
hle/api_version.h
hle/ipc.h
hle/ipc_helpers.h
@@ -167,12 +179,15 @@ add_library(core STATIC
hle/kernel/k_client_port.h
hle/kernel/k_client_session.cpp
hle/kernel/k_client_session.h
+ hle/kernel/k_code_memory.cpp
+ hle/kernel/k_code_memory.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_event.cpp
hle/kernel/k_event.h
hle/kernel/k_handle_table.cpp
hle/kernel/k_handle_table.h
+ hle/kernel/k_light_condition_variable.cpp
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
@@ -225,6 +240,7 @@ add_library(core STATIC
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
+ hle/kernel/k_thread_queue.cpp
hle/kernel/k_thread_queue.h
hle/kernel/k_trace.h
hle/kernel/k_transfer_memory.cpp
@@ -396,12 +412,15 @@ add_library(core STATIC
hle/service/glue/glue.h
hle/service/glue/glue_manager.cpp
hle/service/glue/glue_manager.h
+ hle/service/glue/notif.cpp
+ hle/service/glue/notif.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
+ hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/errors.h
@@ -466,6 +485,8 @@ add_library(core STATIC
hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
+ hle/service/ns/pdm_qry.cpp
+ hle/service/ns/pdm_qry.h
hle/service/ns/pl_u.cpp
hle/service/ns/pl_u.h
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:
num_instructions, MemoryReadCode(pc));
}
+ void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
+ VAddr value) override {
+ switch (op) {
+ case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: {
+ static constexpr u64 ICACHE_LINE_SIZE = 64;
+
+ const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
+ parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
+ break;
+ }
+ case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
+ parent.ClearInstructionCache();
+ break;
+ case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
+ default:
+ LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
+ break;
+ }
+ }
+
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
switch (exception) {
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 @@
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/hardware_interrupt_manager.h"
+#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
@@ -126,7 +127,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
- : kernel{system}, fs_controller{system}, memory{system},
+ : kernel{system}, fs_controller{system}, memory{system}, hid_core{},
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
SystemResultStatus Run() {
@@ -391,6 +392,7 @@ struct System::Impl {
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
std::unique_ptr<Core::DeviceMemory> device_memory;
Core::Memory::Memory memory;
+ Core::HID::HIDCore hid_core;
CpuManager cpu_manager;
std::atomic_bool is_powered_on{};
bool exit_lock = false;
@@ -519,12 +521,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
return impl->kernel.CurrentPhysicalCore().ArmInterface();
}
-std::size_t System::CurrentCoreIndex() const {
- std::size_t core = impl->kernel.GetCurrentHostThreadID();
- ASSERT(core < Core::Hardware::NUM_CPU_CORES);
- return core;
-}
-
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
return impl->kernel.CurrentPhysicalCore();
}
@@ -615,6 +611,14 @@ const Kernel::KernelCore& System::Kernel() const {
return impl->kernel;
}
+HID::HIDCore& System::HIDCore() {
+ return impl->hid_core;
+}
+
+const HID::HIDCore& System::HIDCore() const {
+ return impl->hid_core;
+}
+
Timing::CoreTiming& System::CoreTiming() {
return impl->core_timing;
}
@@ -825,8 +829,6 @@ void System::ApplySettings() {
if (IsPoweredOn()) {
Renderer().RefreshBaseSettings();
}
-
- Service::HID::ReloadInputDevices();
}
} // 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 {
class InterruptManager;
}
+namespace Core::HID {
+class HIDCore;
+}
+
namespace Core {
class ARM_Interface;
@@ -204,9 +208,6 @@ public:
/// Gets an ARM interface to the CPU core that is currently running
[[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
- /// Gets the index of the currently running CPU core
- [[nodiscard]] std::size_t CurrentCoreIndex() const;
-
/// Gets the physical core for the CPU core that is currently running
[[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
@@ -285,6 +286,12 @@ public:
/// Provides a constant reference to the kernel instance.
[[nodiscard]] const Kernel::KernelCore& Kernel() const;
+ /// Gets a mutable reference to the HID interface.
+ [[nodiscard]] HID::HIDCore& HIDCore();
+
+ /// Gets an immutable reference to the HID interface.
+ [[nodiscard]] const HID::HIDCore& HIDCore() const;
+
/// Provides a reference to the internal PerfStats instance.
[[nodiscard]] Core::PerfStats& GetPerfStats();
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() {
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
- physical_core->ArmInterface().ClearExclusiveState();
- kernel.CurrentScheduler()->RescheduleCurrentCore();
+ {
+ Kernel::KScopedDisableDispatch dd(kernel);
+ physical_core->ArmInterface().ClearExclusiveState();
+ }
}
}
void CpuManager::MultiCoreRunIdleThread() {
auto& kernel = system.Kernel();
while (true) {
- auto& physical_core = kernel.CurrentPhysicalCore();
- physical_core.Idle();
- kernel.CurrentScheduler()->RescheduleCurrentCore();
+ Kernel::KScopedDisableDispatch dd(kernel);
+ kernel.CurrentPhysicalCore().Idle();
}
}
@@ -135,12 +136,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
while (true) {
- auto core = kernel.GetCurrentHostThreadID();
+ auto core = kernel.CurrentPhysicalCoreIndex();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
- ASSERT(core == kernel.GetCurrentHostThreadID());
+ ASSERT(core == kernel.CurrentPhysicalCoreIndex());
scheduler.RescheduleCurrentCore();
}
}
@@ -346,13 +347,9 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
sc_sync_first_use = false;
}
- // Abort if emulation was killed before the session really starts
- if (!system.IsPoweredOn()) {
- return;
- }
-
+ // Emulation was stopped
if (stop_token.stop_requested()) {
- break;
+ return;
}
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 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/frontend/applets/controller.h"
-#include "core/hle/service/hid/controllers/npad.h"
-#include "core/hle/service/hid/hid.h"
-#include "core/hle/service/sm/sm.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hid/hid_types.h"
namespace Core::Frontend {
ControllerApplet::~ControllerApplet() = default;
-DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_)
- : service_manager{service_manager_} {}
+DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_core{hid_core_} {}
DefaultControllerApplet::~DefaultControllerApplet() = default;
@@ -22,24 +21,20 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
const ControllerParameters& parameters) const {
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
- auto& npad =
- service_manager.GetService<Service::HID::Hid>("hid")
- ->GetAppletResource()
- ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
-
- auto& players = Settings::values.players.GetValue();
-
const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players;
// Disconnect Handheld first.
- npad.DisconnectNpadAtIndex(8);
+ auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ handheld->Disconnect();
// Deduce the best configuration based on the input parameters.
- for (std::size_t index = 0; index < players.size() - 2; ++index) {
+ for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
+ auto* controller = hid_core.GetEmulatedControllerByIndex(index);
+
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
- npad.DisconnectNpadAtIndex(index);
+ controller->Disconnect();
// Only connect the minimum number of required players.
if (index >= min_supported_players) {
@@ -49,27 +44,27 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
// Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) {
- npad.AddNewControllerAt(
- npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
+ controller->Connect();
} else if (parameters.allow_dual_joycons) {
- npad.AddNewControllerAt(
- npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
+ controller->Connect();
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
// Assign left joycons to even player indices and right joycons to odd player indices.
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
// a right Joycon for Player 2 in 2 Player Assist mode.
if (index % 2 == 0) {
- npad.AddNewControllerAt(
- npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
+ controller->Connect();
} else {
- npad.AddNewControllerAt(
- npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
+ controller->Connect();
}
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
!Settings::values.use_docked_mode.GetValue()) {
// We should *never* reach here under any normal circumstances.
- npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
- index);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
+ controller->Connect();
} else {
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
}
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 @@
#include "common/common_types.h"
-namespace Service::SM {
-class ServiceManager;
+namespace Core::HID {
+class HIDCore;
}
namespace Core::Frontend {
@@ -44,14 +44,14 @@ public:
class DefaultControllerApplet final : public ControllerApplet {
public:
- explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_);
+ explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
~DefaultControllerApplet() override;
void ReconfigureControllers(std::function<void()> callback,
const ControllerParameters& parameters) const override;
private:
- Service::SM::ServiceManager& service_manager;
+ HID::HIDCore& hid_core;
};
} // 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 @@
// Refer to the license.txt file included.
#include <mutex>
-#include "common/settings.h"
#include "core/frontend/emu_window.h"
-#include "core/frontend/input.h"
namespace Core::Frontend {
GraphicsContext::~GraphicsContext() = default;
-class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
- public std::enable_shared_from_this<TouchState> {
-public:
- std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
- return std::make_unique<Device>(shared_from_this());
- }
-
- std::mutex mutex;
-
- Input::TouchStatus status;
-
-private:
- class Device : public Input::TouchDevice {
- public:
- explicit Device(std::weak_ptr<TouchState>&& touch_state_) : touch_state(touch_state_) {}
- Input::TouchStatus GetStatus() const override {
- if (auto state = touch_state.lock()) {
- std::lock_guard guard{state->mutex};
- return state->status;
- }
- return {};
- }
-
- private:
- std::weak_ptr<TouchState> touch_state;
- };
-};
-
EmuWindow::EmuWindow() {
// TODO: Find a better place to set this.
config.min_client_area_size =
std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
active_config = config;
- touch_state = std::make_shared<TouchState>();
- Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
-}
-
-EmuWindow::~EmuWindow() {
- Input::UnregisterFactory<Input::TouchDevice>("emu_window");
-}
-
-/**
- * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
- * @param layout FramebufferLayout object describing the framebuffer size and screen positions
- * @param framebuffer_x Framebuffer x-coordinate to check
- * @param framebuffer_y Framebuffer y-coordinate to check
- * @return True if the coordinates are within the touchpad, otherwise false
- */
-static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, u32 framebuffer_x,
- u32 framebuffer_y) {
- return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom &&
- framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right);
-}
-
-std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
- new_x = std::max(new_x, framebuffer_layout.screen.left);
- new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
-
- new_y = std::max(new_y, framebuffer_layout.screen.top);
- new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
-
- return std::make_pair(new_x, new_y);
}
-void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
- if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
- return;
- }
- if (id >= touch_state->status.size()) {
- return;
- }
+EmuWindow::~EmuWindow() {}
- std::lock_guard guard{touch_state->mutex};
+std::pair<f32, f32> EmuWindow::MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const {
+ std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
const float x =
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
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) {
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
- touch_state->status[id] = std::make_tuple(x, y, true);
-}
-
-void EmuWindow::TouchReleased(size_t id) {
- if (id >= touch_state->status.size()) {
- return;
- }
- std::lock_guard guard{touch_state->mutex};
- touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
+ return std::make_pair(x, y);
}
-void EmuWindow::TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
- if (id >= touch_state->status.size()) {
- return;
- }
-
- if (!std::get<2>(touch_state->status[id])) {
- return;
- }
+std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
+ new_x = std::max(new_x, framebuffer_layout.screen.left);
+ new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
- if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
- std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
- }
+ new_y = std::max(new_y, framebuffer_layout.screen.top);
+ new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
- TouchPressed(framebuffer_x, framebuffer_y, id);
+ return std::make_pair(new_x, new_y);
}
void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 8a86a1d27..e413a520a 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -113,28 +113,6 @@ public:
virtual bool IsShown() const = 0;
/**
- * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
- * @param framebuffer_x Framebuffer x-coordinate that was pressed
- * @param framebuffer_y Framebuffer y-coordinate that was pressed
- * @param id Touch event ID
- */
- void TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id);
-
- /**
- * Signal that a touch released event has occurred (e.g. mouse click released)
- * @param id Touch event ID
- */
- void TouchReleased(size_t id);
-
- /**
- * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
- * @param framebuffer_x Framebuffer x-coordinate
- * @param framebuffer_y Framebuffer y-coordinate
- * @param id Touch event ID
- */
- void TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id);
-
- /**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in
* another thread
@@ -212,6 +190,11 @@ protected:
client_area_height = size.second;
}
+ /**
+ * Converts a screen postion into the equivalent touchscreen position.
+ */
+ std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
+
WindowSystemInfo window_info;
private:
@@ -237,9 +220,6 @@ private:
WindowConfig config; ///< Internal configuration (changes pending for being applied in
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
-
- class TouchState;
- std::shared_ptr<TouchState> touch_state;
};
} // 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) {
ASSERT(height > 0);
// The drawing code needs at least somewhat valid values for both screens
// so just calculate them both even if the other isn't showing.
- FramebufferLayout res{width, height, false, {}};
+ FramebufferLayout res{
+ .width = width,
+ .height = height,
+ .screen = {},
+ .is_srgb = false,
+ };
const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
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 {
struct FramebufferLayout {
u32 width{ScreenUndocked::Width};
u32 height{ScreenUndocked::Height};
- bool is_srgb{};
-
Common::Rectangle<u32> screen;
-
- /**
- * Returns the ration of pixel size of the screen, compared to the native size of the undocked
- * Switch screen.
- */
- float GetScalingRatio() const {
- return static_cast<float>(screen.GetWidth()) / ScreenUndocked::Width;
- }
+ bool is_srgb{};
};
/**
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 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <tuple>
-#include <unordered_map>
-#include <utility>
-#include "common/logging/log.h"
-#include "common/param_package.h"
-#include "common/quaternion.h"
-#include "common/vector_math.h"
-
-namespace Input {
-
-enum class AnalogDirection : u8 {
- RIGHT,
- LEFT,
- UP,
- DOWN,
-};
-struct AnalogProperties {
- float deadzone;
- float range;
- float threshold;
-};
-template <typename StatusType>
-struct InputCallback {
- std::function<void(StatusType)> on_change;
-};
-
-/// An abstract class template for an input device (a button, an analog input, etc.).
-template <typename StatusType>
-class InputDevice {
-public:
- virtual ~InputDevice() = default;
- virtual StatusType GetStatus() const {
- return {};
- }
- virtual StatusType GetRawStatus() const {
- return GetStatus();
- }
- virtual AnalogProperties GetAnalogProperties() const {
- return {};
- }
- virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
- return {};
- }
- virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low,
- [[maybe_unused]] f32 amp_high,
- [[maybe_unused]] f32 freq_high) const {
- return {};
- }
- void SetCallback(InputCallback<StatusType> callback_) {
- callback = std::move(callback_);
- }
- void TriggerOnChange() {
- if (callback.on_change) {
- callback.on_change(GetStatus());
- }
- }
-
-private:
- InputCallback<StatusType> callback;
-};
-
-/// An abstract class template for a factory that can create input devices.
-template <typename InputDeviceType>
-class Factory {
-public:
- virtual ~Factory() = default;
- virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
-};
-
-namespace Impl {
-
-template <typename InputDeviceType>
-using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
-
-template <typename InputDeviceType>
-struct FactoryList {
- static FactoryListType<InputDeviceType> list;
-};
-
-template <typename InputDeviceType>
-FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
-
-} // namespace Impl
-
-/**
- * Registers an input device factory.
- * @tparam InputDeviceType the type of input devices the factory can create
- * @param name the name of the factory. Will be used to match the "engine" parameter when creating
- * a device
- * @param factory the factory object to register
- */
-template <typename InputDeviceType>
-void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
- auto pair = std::make_pair(name, std::move(factory));
- if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
- LOG_ERROR(Input, "Factory '{}' already registered", name);
- }
-}
-
-/**
- * Unregisters an input device factory.
- * @tparam InputDeviceType the type of input devices the factory can create
- * @param name the name of the factory to unregister
- */
-template <typename InputDeviceType>
-void UnregisterFactory(const std::string& name) {
- if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
- LOG_ERROR(Input, "Factory '{}' not registered", name);
- }
-}
-
-/**
- * Create an input device from given paramters.
- * @tparam InputDeviceType the type of input devices to create
- * @param params a serialized ParamPackage string contains all parameters for creating the device
- */
-template <typename InputDeviceType>
-std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
- const Common::ParamPackage package(params);
- const std::string engine = package.Get("engine", "null");
- const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
- const auto pair = factory_list.find(engine);
- if (pair == factory_list.end()) {
- if (engine != "null") {
- LOG_ERROR(Input, "Unknown engine name: {}", engine);
- }
- return std::make_unique<InputDeviceType>();
- }
- return pair->second->Create(package);
-}
-
-/**
- * A button device is an input device that returns bool as status.
- * true for pressed; false for released.
- */
-using ButtonDevice = InputDevice<bool>;
-
-/**
- * An analog device is an input device that returns a tuple of x and y coordinates as status. The
- * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
- * direction
- */
-using AnalogDevice = InputDevice<std::tuple<float, float>>;
-
-/**
- * A vibration device is an input device that returns an unsigned byte as status.
- * It represents whether the vibration device supports vibration or not.
- * If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
- */
-using VibrationDevice = InputDevice<u8>;
-
-/**
- * A motion status is an object that returns a tuple of accelerometer state vector,
- * gyroscope state vector, rotation state vector, orientation state matrix and quaterion state
- * vector.
- *
- * For both 3D vectors:
- * x+ is the same direction as RIGHT on D-pad.
- * y+ is normal to the touch screen, pointing outward.
- * z+ is the same direction as UP on D-pad.
- *
- * For accelerometer state vector
- * Units: g (gravitational acceleration)
- *
- * For gyroscope state vector:
- * Orientation is determined by right-hand rule.
- * Units: deg/sec
- *
- * For rotation state vector
- * Units: rotations
- *
- * For orientation state matrix
- * x vector
- * y vector
- * z vector
- *
- * For quaternion state vector
- * xyz vector
- * w float
- */
-using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
- std::array<Common::Vec3f, 3>, Common::Quaternion<f32>>;
-
-/**
- * A motion device is an input device that returns a motion status object
- */
-using MotionDevice = InputDevice<MotionStatus>;
-
-/**
- * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
- * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
- * pressed.
- */
-using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
-
-/**
- * A touch device is an input device that returns a touch status object
- */
-using TouchDevice = InputDevice<TouchStatus>;
-
-/**
- * A mouse device is an input device that returns a tuple of two floats and four ints.
- * The first two floats are X and Y device coordinates of the mouse (from 0-1).
- * The s32s are the mouse wheel.
- */
-using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
-
-} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include "common/settings.h"
+#include "core/hid/emulated_console.h"
+#include "core/hid/input_converter.h"
+
+namespace Core::HID {
+EmulatedConsole::EmulatedConsole() = default;
+
+EmulatedConsole::~EmulatedConsole() = default;
+
+void EmulatedConsole::ReloadFromSettings() {
+ // Using first motion device from player 1. No need to assign any unique config at the moment
+ const auto& player = Settings::values.players.GetValue()[0];
+ motion_params = Common::ParamPackage(player.motions[0]);
+
+ ReloadInput();
+}
+
+void EmulatedConsole::SetTouchParams() {
+ // TODO(german77): Support any number of fingers
+ std::size_t index = 0;
+
+ // Hardcode mouse, touchscreen and cemuhook parameters
+ if (!Settings::values.mouse_enabled) {
+ // We can't use mouse as touch if native mouse is enabled
+ touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
+ }
+ touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
+ touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
+
+ const auto button_index =
+ static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
+ const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
+
+ // Map the rest of the fingers from touch from button configuration
+ for (const auto& config_entry : touch_buttons) {
+ if (index >= touch_params.size()) {
+ continue;
+ }
+ Common::ParamPackage params{config_entry};
+ Common::ParamPackage touch_button_params;
+ const int x = params.Get("x", 0);
+ const int y = params.Get("y", 0);
+ params.Erase("x");
+ params.Erase("y");
+ touch_button_params.Set("engine", "touch_from_button");
+ touch_button_params.Set("button", params.Serialize());
+ touch_button_params.Set("x", x);
+ touch_button_params.Set("y", y);
+ touch_button_params.Set("touch_id", static_cast<int>(index));
+ touch_params[index] = touch_button_params;
+ index++;
+ }
+}
+
+void EmulatedConsole::ReloadInput() {
+ // If you load any device here add the equivalent to the UnloadInput() function
+ SetTouchParams();
+
+ motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
+ if (motion_devices) {
+ motion_devices->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
+ });
+ }
+
+ // Unique index for identifying touch device source
+ std::size_t index = 0;
+ for (auto& touch_device : touch_devices) {
+ touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]);
+ if (!touch_device) {
+ continue;
+ }
+ touch_device->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetTouch(callback, index);
+ },
+ });
+ index++;
+ }
+}
+
+void EmulatedConsole::UnloadInput() {
+ motion_devices.reset();
+ for (auto& touch : touch_devices) {
+ touch.reset();
+ }
+}
+
+void EmulatedConsole::EnableConfiguration() {
+ is_configuring = true;
+ SaveCurrentConfig();
+}
+
+void EmulatedConsole::DisableConfiguration() {
+ is_configuring = false;
+}
+
+bool EmulatedConsole::IsConfiguring() const {
+ return is_configuring;
+}
+
+void EmulatedConsole::SaveCurrentConfig() {
+ if (!is_configuring) {
+ return;
+ }
+}
+
+void EmulatedConsole::RestoreConfig() {
+ if (!is_configuring) {
+ return;
+ }
+ ReloadFromSettings();
+}
+
+Common::ParamPackage EmulatedConsole::GetMotionParam() const {
+ return motion_params;
+}
+
+void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
+ motion_params = param;
+ ReloadInput();
+}
+
+void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
+ std::lock_guard lock{mutex};
+ auto& raw_status = console.motion_values.raw_status;
+ auto& emulated = console.motion_values.emulated;
+
+ raw_status = TransformToMotion(callback);
+ emulated.SetAcceleration(Common::Vec3f{
+ raw_status.accel.x.value,
+ raw_status.accel.y.value,
+ raw_status.accel.z.value,
+ });
+ emulated.SetGyroscope(Common::Vec3f{
+ raw_status.gyro.x.value,
+ raw_status.gyro.y.value,
+ raw_status.gyro.z.value,
+ });
+ emulated.UpdateRotation(raw_status.delta_timestamp);
+ emulated.UpdateOrientation(raw_status.delta_timestamp);
+
+ if (is_configuring) {
+ TriggerOnChange(ConsoleTriggerType::Motion);
+ return;
+ }
+
+ auto& motion = console.motion_state;
+ motion.accel = emulated.GetAcceleration();
+ motion.gyro = emulated.GetGyroscope();
+ motion.rotation = emulated.GetGyroscope();
+ motion.orientation = emulated.GetOrientation();
+ motion.quaternion = emulated.GetQuaternion();
+ motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
+
+ TriggerOnChange(ConsoleTriggerType::Motion);
+}
+
+void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
+ if (index >= console.touch_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+
+ console.touch_values[index] = TransformToTouch(callback);
+
+ if (is_configuring) {
+ TriggerOnChange(ConsoleTriggerType::Touch);
+ return;
+ }
+
+ // TODO(german77): Remap touch id in sequential order
+ console.touch_state[index] = {
+ .position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
+ .id = static_cast<u32>(console.touch_values[index].id),
+ .pressed = console.touch_values[index].pressed.value,
+ };
+
+ TriggerOnChange(ConsoleTriggerType::Touch);
+}
+
+ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
+ return console.motion_values;
+}
+
+TouchValues EmulatedConsole::GetTouchValues() const {
+ return console.touch_values;
+}
+
+ConsoleMotion EmulatedConsole::GetMotion() const {
+ return console.motion_state;
+}
+
+TouchFingerState EmulatedConsole::GetTouch() const {
+ return console.touch_state;
+}
+
+void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
+ for (const auto& poller_pair : callback_list) {
+ const ConsoleUpdateCallback& poller = poller_pair.second;
+ if (poller.on_change) {
+ poller.on_change(type);
+ }
+ }
+}
+
+int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
+ std::lock_guard lock{mutex};
+ callback_list.insert_or_assign(last_callback_key, update_callback);
+ return last_callback_key++;
+}
+
+void EmulatedConsole::DeleteCallback(int key) {
+ std::lock_guard lock{mutex};
+ const auto& iterator = callback_list.find(key);
+ if (iterator == callback_list.end()) {
+ LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
+ return;
+ }
+ callback_list.erase(iterator);
+}
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "common/input.h"
+#include "common/param_package.h"
+#include "common/point.h"
+#include "common/quaternion.h"
+#include "common/vector_math.h"
+#include "core/hid/hid_types.h"
+#include "core/hid/motion_input.h"
+
+namespace Core::HID {
+
+struct ConsoleMotionInfo {
+ Common::Input::MotionStatus raw_status{};
+ MotionInput emulated{};
+};
+
+using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
+using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
+
+using ConsoleMotionParams = Common::ParamPackage;
+using TouchParams = std::array<Common::ParamPackage, 16>;
+
+using ConsoleMotionValues = ConsoleMotionInfo;
+using TouchValues = std::array<Common::Input::TouchStatus, 16>;
+
+struct TouchFinger {
+ u64 last_touch{};
+ Common::Point<float> position{};
+ u32 id{};
+ TouchAttribute attribute{};
+ bool pressed{};
+};
+
+// Contains all motion related data that is used on the services
+struct ConsoleMotion {
+ Common::Vec3f accel{};
+ Common::Vec3f gyro{};
+ Common::Vec3f rotation{};
+ std::array<Common::Vec3f, 3> orientation{};
+ Common::Quaternion<f32> quaternion{};
+ bool is_at_rest{};
+};
+
+using TouchFingerState = std::array<TouchFinger, 16>;
+
+struct ConsoleStatus {
+ // Data from input_common
+ ConsoleMotionValues motion_values{};
+ TouchValues touch_values{};
+
+ // Data for HID services
+ ConsoleMotion motion_state{};
+ TouchFingerState touch_state{};
+};
+
+enum class ConsoleTriggerType {
+ Motion,
+ Touch,
+ All,
+};
+
+struct ConsoleUpdateCallback {
+ std::function<void(ConsoleTriggerType)> on_change;
+};
+
+class EmulatedConsole {
+public:
+ /**
+ * Contains all input data within the emulated switch console tablet such as touch and motion
+ */
+ explicit EmulatedConsole();
+ ~EmulatedConsole();
+
+ YUZU_NON_COPYABLE(EmulatedConsole);
+ YUZU_NON_MOVEABLE(EmulatedConsole);
+
+ /// Removes all callbacks created from input devices
+ void UnloadInput();
+
+ /**
+ * Sets the emulated console into configuring mode
+ * This prevents the modification of the HID state of the emulated console by input commands
+ */
+ void EnableConfiguration();
+
+ /// Returns the emulated console into normal mode, allowing the modification of the HID state
+ void DisableConfiguration();
+
+ /// Returns true if the emulated console is in configuring mode
+ bool IsConfiguring() const;
+
+ /// Reload all input devices
+ void ReloadInput();
+
+ /// Overrides current mapped devices with the stored configuration and reloads all input devices
+ void ReloadFromSettings();
+
+ /// Saves the current mapped configuration
+ void SaveCurrentConfig();
+
+ /// Reverts any mapped changes made that weren't saved
+ void RestoreConfig();
+
+ // Returns the current mapped motion device
+ Common::ParamPackage GetMotionParam() const;
+
+ /**
+ * Updates the current mapped motion device
+ * @param param ParamPackage with controller data to be mapped
+ */
+ void SetMotionParam(Common::ParamPackage param);
+
+ /// Returns the latest status of motion input from the console with parameters
+ ConsoleMotionValues GetMotionValues() const;
+
+ /// Returns the latest status of touch input from the console with parameters
+ TouchValues GetTouchValues() const;
+
+ /// Returns the latest status of motion input from the console
+ ConsoleMotion GetMotion() const;
+
+ /// Returns the latest status of touch input from the console
+ TouchFingerState GetTouch() const;
+
+ /**
+ * Adds a callback to the list of events
+ * @param update_callback A ConsoleUpdateCallback that will be triggered
+ * @return an unique key corresponding to the callback index in the list
+ */
+ int SetCallback(ConsoleUpdateCallback update_callback);
+
+ /**
+ * Removes a callback from the list stopping any future events to this object
+ * @param key Key corresponding to the callback index in the list
+ */
+ void DeleteCallback(int key);
+
+private:
+ /// Creates and stores the touch params
+ void SetTouchParams();
+
+ /**
+ * Updates the motion status of the console
+ * @param callback A CallbackStatus containing gyro and accelerometer data
+ */
+ void SetMotion(const Common::Input::CallbackStatus& callback);
+
+ /**
+ * Updates the touch status of the console
+ * @param callback A CallbackStatus containing the touch position
+ * @param index Finger ID to be updated
+ */
+ void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
+
+ /**
+ * Triggers a callback that something has changed on the console status
+ * @param type Input type of the event to trigger
+ */
+ void TriggerOnChange(ConsoleTriggerType type);
+
+ bool is_configuring{false};
+ f32 motion_sensitivity{0.01f};
+
+ ConsoleMotionParams motion_params;
+ TouchParams touch_params;
+
+ ConsoleMotionDevices motion_devices;
+ TouchDevices touch_devices;
+
+ mutable std::mutex mutex;
+ std::unordered_map<int, ConsoleUpdateCallback> callback_list;
+ int last_callback_key = 0;
+
+ // Stores the current status of all console input
+ ConsoleStatus console;
+};
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/input_converter.h"
+
+namespace Core::HID {
+constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
+constexpr s32 HID_TRIGGER_MAX = 0x7fff;
+
+EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
+
+EmulatedController::~EmulatedController() = default;
+
+NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
+ switch (type) {
+ case Settings::ControllerType::ProController:
+ return NpadStyleIndex::ProController;
+ case Settings::ControllerType::DualJoyconDetached:
+ return NpadStyleIndex::JoyconDual;
+ case Settings::ControllerType::LeftJoycon:
+ return NpadStyleIndex::JoyconLeft;
+ case Settings::ControllerType::RightJoycon:
+ return NpadStyleIndex::JoyconRight;
+ case Settings::ControllerType::Handheld:
+ return NpadStyleIndex::Handheld;
+ case Settings::ControllerType::GameCube:
+ return NpadStyleIndex::GameCube;
+ case Settings::ControllerType::Pokeball:
+ return NpadStyleIndex::Pokeball;
+ case Settings::ControllerType::NES:
+ return NpadStyleIndex::NES;
+ case Settings::ControllerType::SNES:
+ return NpadStyleIndex::SNES;
+ case Settings::ControllerType::N64:
+ return NpadStyleIndex::N64;
+ case Settings::ControllerType::SegaGenesis:
+ return NpadStyleIndex::SegaGenesis;
+ default:
+ return NpadStyleIndex::ProController;
+ }
+}
+
+Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
+ switch (type) {
+ case NpadStyleIndex::ProController:
+ return Settings::ControllerType::ProController;
+ case NpadStyleIndex::JoyconDual:
+ return Settings::ControllerType::DualJoyconDetached;
+ case NpadStyleIndex::JoyconLeft:
+ return Settings::ControllerType::LeftJoycon;
+ case NpadStyleIndex::JoyconRight:
+ return Settings::ControllerType::RightJoycon;
+ case NpadStyleIndex::Handheld:
+ return Settings::ControllerType::Handheld;
+ case NpadStyleIndex::GameCube:
+ return Settings::ControllerType::GameCube;
+ case NpadStyleIndex::Pokeball:
+ return Settings::ControllerType::Pokeball;
+ case NpadStyleIndex::NES:
+ return Settings::ControllerType::NES;
+ case NpadStyleIndex::SNES:
+ return Settings::ControllerType::SNES;
+ case NpadStyleIndex::N64:
+ return Settings::ControllerType::N64;
+ case NpadStyleIndex::SegaGenesis:
+ return Settings::ControllerType::SegaGenesis;
+ default:
+ return Settings::ControllerType::ProController;
+ }
+}
+
+void EmulatedController::ReloadFromSettings() {
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ const auto& player = Settings::values.players.GetValue()[player_index];
+
+ for (std::size_t index = 0; index < player.buttons.size(); ++index) {
+ button_params[index] = Common::ParamPackage(player.buttons[index]);
+ }
+ for (std::size_t index = 0; index < player.analogs.size(); ++index) {
+ stick_params[index] = Common::ParamPackage(player.analogs[index]);
+ }
+ for (std::size_t index = 0; index < player.motions.size(); ++index) {
+ motion_params[index] = Common::ParamPackage(player.motions[index]);
+ }
+
+ controller.colors_state.left = {
+ .body = player.body_color_left,
+ .button = player.button_color_left,
+ };
+
+ controller.colors_state.right = {
+ .body = player.body_color_right,
+ .button = player.button_color_right,
+ };
+
+ controller.colors_state.fullkey = controller.colors_state.left;
+
+ // Other or debug controller should always be a pro controller
+ if (npad_id_type != NpadIdType::Other) {
+ SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
+ } else {
+ SetNpadStyleIndex(NpadStyleIndex::ProController);
+ }
+
+ if (player.connected) {
+ Connect();
+ } else {
+ Disconnect();
+ }
+
+ ReloadInput();
+}
+
+void EmulatedController::LoadDevices() {
+ // TODO(german77): Use more buttons to detect the correct device
+ const auto left_joycon = button_params[Settings::NativeButton::DRight];
+ const auto right_joycon = button_params[Settings::NativeButton::A];
+
+ // Triggers for GC controllers
+ trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
+ trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
+
+ battery_params[LeftIndex] = left_joycon;
+ battery_params[RightIndex] = right_joycon;
+ battery_params[LeftIndex].Set("battery", true);
+ battery_params[RightIndex].Set("battery", true);
+
+ output_params[LeftIndex] = left_joycon;
+ output_params[RightIndex] = right_joycon;
+ output_params[LeftIndex].Set("output", true);
+ output_params[RightIndex].Set("output", true);
+
+ LoadTASParams();
+
+ std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
+ button_params.begin() + Settings::NativeButton::BUTTON_NS_END,
+ button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
+ std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
+ stick_params.begin() + Settings::NativeAnalog::STICK_HID_END,
+ stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
+ std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
+ motion_params.begin() + Settings::NativeMotion::MOTION_HID_END,
+ motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
+ std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
+ Common::Input::CreateDevice<Common::Input::InputDevice>);
+ std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(),
+ Common::Input::CreateDevice<Common::Input::InputDevice>);
+ std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
+ Common::Input::CreateDevice<Common::Input::OutputDevice>);
+
+ // Initialize TAS devices
+ std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(),
+ Common::Input::CreateDevice<Common::Input::InputDevice>);
+ std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(),
+ Common::Input::CreateDevice<Common::Input::InputDevice>);
+}
+
+void EmulatedController::LoadTASParams() {
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ Common::ParamPackage common_params{};
+ common_params.Set("engine", "tas");
+ common_params.Set("port", static_cast<int>(player_index));
+ for (auto& param : tas_button_params) {
+ param = common_params;
+ }
+ for (auto& param : tas_stick_params) {
+ param = common_params;
+ }
+
+ // TODO(german77): Replace this with an input profile or something better
+ tas_button_params[Settings::NativeButton::A].Set("button", 0);
+ tas_button_params[Settings::NativeButton::B].Set("button", 1);
+ tas_button_params[Settings::NativeButton::X].Set("button", 2);
+ tas_button_params[Settings::NativeButton::Y].Set("button", 3);
+ tas_button_params[Settings::NativeButton::LStick].Set("button", 4);
+ tas_button_params[Settings::NativeButton::RStick].Set("button", 5);
+ tas_button_params[Settings::NativeButton::L].Set("button", 6);
+ tas_button_params[Settings::NativeButton::R].Set("button", 7);
+ tas_button_params[Settings::NativeButton::ZL].Set("button", 8);
+ tas_button_params[Settings::NativeButton::ZR].Set("button", 9);
+ tas_button_params[Settings::NativeButton::Plus].Set("button", 10);
+ tas_button_params[Settings::NativeButton::Minus].Set("button", 11);
+ tas_button_params[Settings::NativeButton::DLeft].Set("button", 12);
+ tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
+ tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
+ tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
+ tas_button_params[Settings::NativeButton::SL].Set("button", 16);
+ tas_button_params[Settings::NativeButton::SR].Set("button", 17);
+ tas_button_params[Settings::NativeButton::Home].Set("button", 18);
+ tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
+
+ tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
+ tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
+ tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
+ tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
+}
+
+void EmulatedController::ReloadInput() {
+ // If you load any device here add the equivalent to the UnloadInput() function
+ LoadDevices();
+ for (std::size_t index = 0; index < button_devices.size(); ++index) {
+ if (!button_devices[index]) {
+ continue;
+ }
+ const auto uuid = Common::UUID{button_params[index].Get("guid", "")};
+ button_devices[index]->SetCallback({
+ .on_change =
+ [this, index, uuid](const Common::Input::CallbackStatus& callback) {
+ SetButton(callback, index, uuid);
+ },
+ });
+ button_devices[index]->ForceUpdate();
+ }
+
+ for (std::size_t index = 0; index < stick_devices.size(); ++index) {
+ if (!stick_devices[index]) {
+ continue;
+ }
+ const auto uuid = Common::UUID{stick_params[index].Get("guid", "")};
+ stick_devices[index]->SetCallback({
+ .on_change =
+ [this, index, uuid](const Common::Input::CallbackStatus& callback) {
+ SetStick(callback, index, uuid);
+ },
+ });
+ stick_devices[index]->ForceUpdate();
+ }
+
+ for (std::size_t index = 0; index < trigger_devices.size(); ++index) {
+ if (!trigger_devices[index]) {
+ continue;
+ }
+ const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")};
+ trigger_devices[index]->SetCallback({
+ .on_change =
+ [this, index, uuid](const Common::Input::CallbackStatus& callback) {
+ SetTrigger(callback, index, uuid);
+ },
+ });
+ trigger_devices[index]->ForceUpdate();
+ }
+
+ for (std::size_t index = 0; index < battery_devices.size(); ++index) {
+ if (!battery_devices[index]) {
+ continue;
+ }
+ battery_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetBattery(callback, index);
+ },
+ });
+ battery_devices[index]->ForceUpdate();
+ }
+
+ for (std::size_t index = 0; index < motion_devices.size(); ++index) {
+ if (!motion_devices[index]) {
+ continue;
+ }
+ motion_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetMotion(callback, index);
+ },
+ });
+ motion_devices[index]->ForceUpdate();
+ }
+
+ // Use a common UUID for TAS
+ const auto tas_uuid = Common::UUID{0x0, 0x7A5};
+
+ // Register TAS devices. No need to force update
+ for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
+ if (!tas_button_devices[index]) {
+ continue;
+ }
+ tas_button_devices[index]->SetCallback({
+ .on_change =
+ [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
+ SetButton(callback, index, tas_uuid);
+ },
+ });
+ }
+
+ for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
+ if (!tas_stick_devices[index]) {
+ continue;
+ }
+ tas_stick_devices[index]->SetCallback({
+ .on_change =
+ [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
+ SetStick(callback, index, tas_uuid);
+ },
+ });
+ }
+}
+
+void EmulatedController::UnloadInput() {
+ for (auto& button : button_devices) {
+ button.reset();
+ }
+ for (auto& stick : stick_devices) {
+ stick.reset();
+ }
+ for (auto& motion : motion_devices) {
+ motion.reset();
+ }
+ for (auto& trigger : trigger_devices) {
+ trigger.reset();
+ }
+ for (auto& battery : battery_devices) {
+ battery.reset();
+ }
+ for (auto& output : output_devices) {
+ output.reset();
+ }
+ for (auto& button : tas_button_devices) {
+ button.reset();
+ }
+ for (auto& stick : tas_stick_devices) {
+ stick.reset();
+ }
+}
+
+void EmulatedController::EnableConfiguration() {
+ is_configuring = true;
+ tmp_is_connected = is_connected;
+ tmp_npad_type = npad_type;
+}
+
+void EmulatedController::DisableConfiguration() {
+ is_configuring = false;
+
+ // Apply temporary npad type to the real controller
+ if (tmp_npad_type != npad_type) {
+ if (is_connected) {
+ Disconnect();
+ }
+ SetNpadStyleIndex(tmp_npad_type);
+ }
+
+ // Apply temporary connected status to the real controller
+ if (tmp_is_connected != is_connected) {
+ if (tmp_is_connected) {
+ Connect();
+ return;
+ }
+ Disconnect();
+ }
+}
+
+bool EmulatedController::IsConfiguring() const {
+ return is_configuring;
+}
+
+void EmulatedController::SaveCurrentConfig() {
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ auto& player = Settings::values.players.GetValue()[player_index];
+ player.connected = is_connected;
+ player.controller_type = MapNPadToSettingsType(npad_type);
+ for (std::size_t index = 0; index < player.buttons.size(); ++index) {
+ player.buttons[index] = button_params[index].Serialize();
+ }
+ for (std::size_t index = 0; index < player.analogs.size(); ++index) {
+ player.analogs[index] = stick_params[index].Serialize();
+ }
+ for (std::size_t index = 0; index < player.motions.size(); ++index) {
+ player.motions[index] = motion_params[index].Serialize();
+ }
+}
+
+void EmulatedController::RestoreConfig() {
+ if (!is_configuring) {
+ return;
+ }
+ ReloadFromSettings();
+}
+
+std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
+ EmulatedDeviceIndex device_index) const {
+ std::vector<Common::ParamPackage> devices;
+ for (const auto& param : button_params) {
+ if (!param.Has("engine")) {
+ continue;
+ }
+ const auto devices_it = std::find_if(
+ devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
+ return param.Get("engine", "") == param_.Get("engine", "") &&
+ param.Get("guid", "") == param_.Get("guid", "") &&
+ param.Get("port", 0) == param_.Get("port", 0);
+ });
+ if (devices_it != devices.end()) {
+ continue;
+ }
+ Common::ParamPackage device{};
+ device.Set("engine", param.Get("engine", ""));
+ device.Set("guid", param.Get("guid", ""));
+ device.Set("port", param.Get("port", 0));
+ devices.push_back(device);
+ }
+
+ for (const auto& param : stick_params) {
+ if (!param.Has("engine")) {
+ continue;
+ }
+ if (param.Get("engine", "") == "analog_from_button") {
+ continue;
+ }
+ const auto devices_it = std::find_if(
+ devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
+ return param.Get("engine", "") == param_.Get("engine", "") &&
+ param.Get("guid", "") == param_.Get("guid", "") &&
+ param.Get("port", 0) == param_.Get("port", 0);
+ });
+ if (devices_it != devices.end()) {
+ continue;
+ }
+ Common::ParamPackage device{};
+ device.Set("engine", param.Get("engine", ""));
+ device.Set("guid", param.Get("guid", ""));
+ device.Set("port", param.Get("port", 0));
+ devices.push_back(device);
+ }
+ return devices;
+}
+
+Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const {
+ if (index >= button_params.size()) {
+ return {};
+ }
+ return button_params[index];
+}
+
+Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const {
+ if (index >= stick_params.size()) {
+ return {};
+ }
+ return stick_params[index];
+}
+
+Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const {
+ if (index >= motion_params.size()) {
+ return {};
+ }
+ return motion_params[index];
+}
+
+void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) {
+ if (index >= button_params.size()) {
+ return;
+ }
+ button_params[index] = std::move(param);
+ ReloadInput();
+}
+
+void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) {
+ if (index >= stick_params.size()) {
+ return;
+ }
+ stick_params[index] = std::move(param);
+ ReloadInput();
+}
+
+void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
+ if (index >= motion_params.size()) {
+ return;
+ }
+ motion_params[index] = std::move(param);
+ ReloadInput();
+}
+
+void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
+ Common::UUID uuid) {
+ if (index >= controller.button_values.size()) {
+ return;
+ }
+ {
+ std::lock_guard lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = controller.button_values[index];
+
+ // Only read button values that have the same uuid or are pressed once
+ if (current_status.uuid != uuid) {
+ if (!new_status.value) {
+ return;
+ }
+ }
+
+ current_status.toggle = new_status.toggle;
+ current_status.uuid = uuid;
+
+ // Update button status with current
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
+ }
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
+ }
+
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
+ }
+ }
+
+ if (!value_changed) {
+ return;
+ }
+
+ if (is_configuring) {
+ controller.npad_button_state.raw = NpadButton::None;
+ controller.debug_pad_button_state.raw = 0;
+ TriggerOnChange(ControllerTriggerType::Button, false);
+ return;
+ }
+
+ switch (index) {
+ case Settings::NativeButton::A:
+ controller.npad_button_state.a.Assign(current_status.value);
+ controller.debug_pad_button_state.a.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::B:
+ controller.npad_button_state.b.Assign(current_status.value);
+ controller.debug_pad_button_state.b.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::X:
+ controller.npad_button_state.x.Assign(current_status.value);
+ controller.debug_pad_button_state.x.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Y:
+ controller.npad_button_state.y.Assign(current_status.value);
+ controller.debug_pad_button_state.y.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::LStick:
+ controller.npad_button_state.stick_l.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::RStick:
+ controller.npad_button_state.stick_r.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::L:
+ controller.npad_button_state.l.Assign(current_status.value);
+ controller.debug_pad_button_state.l.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::R:
+ controller.npad_button_state.r.Assign(current_status.value);
+ controller.debug_pad_button_state.r.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::ZL:
+ controller.npad_button_state.zl.Assign(current_status.value);
+ controller.debug_pad_button_state.zl.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::ZR:
+ controller.npad_button_state.zr.Assign(current_status.value);
+ controller.debug_pad_button_state.zr.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Plus:
+ controller.npad_button_state.plus.Assign(current_status.value);
+ controller.debug_pad_button_state.plus.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Minus:
+ controller.npad_button_state.minus.Assign(current_status.value);
+ controller.debug_pad_button_state.minus.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DLeft:
+ controller.npad_button_state.left.Assign(current_status.value);
+ controller.debug_pad_button_state.d_left.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DUp:
+ controller.npad_button_state.up.Assign(current_status.value);
+ controller.debug_pad_button_state.d_up.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DRight:
+ controller.npad_button_state.right.Assign(current_status.value);
+ controller.debug_pad_button_state.d_right.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DDown:
+ controller.npad_button_state.down.Assign(current_status.value);
+ controller.debug_pad_button_state.d_down.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::SL:
+ controller.npad_button_state.left_sl.Assign(current_status.value);
+ controller.npad_button_state.right_sl.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::SR:
+ controller.npad_button_state.left_sr.Assign(current_status.value);
+ controller.npad_button_state.right_sr.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Home:
+ case Settings::NativeButton::Screenshot:
+ break;
+ }
+ }
+ if (!is_connected) {
+ if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
+ Connect();
+ }
+ if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) {
+ Connect();
+ }
+ }
+ TriggerOnChange(ControllerTriggerType::Button, true);
+}
+
+void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
+ Common::UUID uuid) {
+ if (index >= controller.stick_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ const auto stick_value = TransformToStick(callback);
+
+ // Only read stick values that have the same uuid or are over the threshold to avoid flapping
+ if (controller.stick_values[index].uuid != uuid) {
+ if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) {
+ return;
+ }
+ }
+
+ controller.stick_values[index] = stick_value;
+ controller.stick_values[index].uuid = uuid;
+
+ if (is_configuring) {
+ controller.analog_stick_state.left = {};
+ controller.analog_stick_state.right = {};
+ TriggerOnChange(ControllerTriggerType::Stick, false);
+ return;
+ }
+
+ const AnalogStickState stick{
+ .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX),
+ .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX),
+ };
+
+ switch (index) {
+ case Settings::NativeAnalog::LStick:
+ controller.analog_stick_state.left = stick;
+ controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left);
+ controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up);
+ controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right);
+ controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down);
+ break;
+ case Settings::NativeAnalog::RStick:
+ controller.analog_stick_state.right = stick;
+ controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left);
+ controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up);
+ controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right);
+ controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
+ break;
+ }
+
+ TriggerOnChange(ControllerTriggerType::Stick, true);
+}
+
+void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
+ std::size_t index, Common::UUID uuid) {
+ if (index >= controller.trigger_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ const auto trigger_value = TransformToTrigger(callback);
+
+ // Only read trigger values that have the same uuid or are pressed once
+ if (controller.trigger_values[index].uuid != uuid) {
+ if (!trigger_value.pressed.value) {
+ return;
+ }
+ }
+
+ controller.trigger_values[index] = trigger_value;
+ controller.trigger_values[index].uuid = uuid;
+
+ if (is_configuring) {
+ controller.gc_trigger_state.left = 0;
+ controller.gc_trigger_state.right = 0;
+ TriggerOnChange(ControllerTriggerType::Trigger, false);
+ return;
+ }
+
+ const auto& trigger = controller.trigger_values[index];
+
+ switch (index) {
+ case Settings::NativeTrigger::LTrigger:
+ controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
+ controller.npad_button_state.zl.Assign(trigger.pressed.value);
+ break;
+ case Settings::NativeTrigger::RTrigger:
+ controller.gc_trigger_state.right =
+ static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
+ controller.npad_button_state.zr.Assign(trigger.pressed.value);
+ break;
+ }
+
+ TriggerOnChange(ControllerTriggerType::Trigger, true);
+}
+
+void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
+ std::size_t index) {
+ if (index >= controller.motion_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ auto& raw_status = controller.motion_values[index].raw_status;
+ auto& emulated = controller.motion_values[index].emulated;
+
+ raw_status = TransformToMotion(callback);
+ emulated.SetAcceleration(Common::Vec3f{
+ raw_status.accel.x.value,
+ raw_status.accel.y.value,
+ raw_status.accel.z.value,
+ });
+ emulated.SetGyroscope(Common::Vec3f{
+ raw_status.gyro.x.value,
+ raw_status.gyro.y.value,
+ raw_status.gyro.z.value,
+ });
+ emulated.UpdateRotation(raw_status.delta_timestamp);
+ emulated.UpdateOrientation(raw_status.delta_timestamp);
+ force_update_motion = raw_status.force_update;
+
+ if (is_configuring) {
+ TriggerOnChange(ControllerTriggerType::Motion, false);
+ return;
+ }
+
+ auto& motion = controller.motion_state[index];
+ motion.accel = emulated.GetAcceleration();
+ motion.gyro = emulated.GetGyroscope();
+ motion.rotation = emulated.GetRotations();
+ motion.orientation = emulated.GetOrientation();
+ motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
+
+ TriggerOnChange(ControllerTriggerType::Motion, true);
+}
+
+void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
+ std::size_t index) {
+ if (index >= controller.battery_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ controller.battery_values[index] = TransformToBattery(callback);
+
+ if (is_configuring) {
+ TriggerOnChange(ControllerTriggerType::Battery, false);
+ return;
+ }
+
+ bool is_charging = false;
+ bool is_powered = false;
+ NpadBatteryLevel battery_level = 0;
+ switch (controller.battery_values[index]) {
+ case Common::Input::BatteryLevel::Charging:
+ is_charging = true;
+ is_powered = true;
+ battery_level = 6;
+ break;
+ case Common::Input::BatteryLevel::Medium:
+ battery_level = 6;
+ break;
+ case Common::Input::BatteryLevel::Low:
+ battery_level = 4;
+ break;
+ case Common::Input::BatteryLevel::Critical:
+ battery_level = 2;
+ break;
+ case Common::Input::BatteryLevel::Empty:
+ battery_level = 0;
+ break;
+ case Common::Input::BatteryLevel::None:
+ case Common::Input::BatteryLevel::Full:
+ default:
+ is_powered = true;
+ battery_level = 8;
+ break;
+ }
+
+ switch (index) {
+ case LeftIndex:
+ controller.battery_state.left = {
+ .is_powered = is_powered,
+ .is_charging = is_charging,
+ .battery_level = battery_level,
+ };
+ break;
+ case RightIndex:
+ controller.battery_state.right = {
+ .is_powered = is_powered,
+ .is_charging = is_charging,
+ .battery_level = battery_level,
+ };
+ break;
+ case DualIndex:
+ controller.battery_state.dual = {
+ .is_powered = is_powered,
+ .is_charging = is_charging,
+ .battery_level = battery_level,
+ };
+ break;
+ }
+ TriggerOnChange(ControllerTriggerType::Battery, true);
+}
+
+bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
+ if (device_index >= output_devices.size()) {
+ return false;
+ }
+ if (!output_devices[device_index]) {
+ return false;
+ }
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ const auto& player = Settings::values.players.GetValue()[player_index];
+ const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
+
+ if (!player.vibration_enabled) {
+ return false;
+ }
+
+ // Exponential amplification is too strong at low amplitudes. Switch to a linear
+ // amplification if strength is set below 0.7f
+ const Common::Input::VibrationAmplificationType type =
+ strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential
+ : Common::Input::VibrationAmplificationType::Linear;
+
+ const Common::Input::VibrationStatus status = {
+ .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f),
+ .low_frequency = vibration.low_frequency,
+ .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f),
+ .high_frequency = vibration.high_frequency,
+ .type = type,
+ };
+ return output_devices[device_index]->SetVibration(status) ==
+ Common::Input::VibrationError::None;
+}
+
+bool EmulatedController::TestVibration(std::size_t device_index) {
+ if (device_index >= output_devices.size()) {
+ return false;
+ }
+ if (!output_devices[device_index]) {
+ return false;
+ }
+
+ // Send a slight vibration to test for rumble support
+ constexpr Common::Input::VibrationStatus status = {
+ .low_amplitude = 0.001f,
+ .low_frequency = 160.0f,
+ .high_amplitude = 0.001f,
+ .high_frequency = 320.0f,
+ .type = Common::Input::VibrationAmplificationType::Linear,
+ };
+ return output_devices[device_index]->SetVibration(status) ==
+ Common::Input::VibrationError::None;
+}
+
+void EmulatedController::SetLedPattern() {
+ for (auto& device : output_devices) {
+ if (!device) {
+ continue;
+ }
+
+ const LedPattern pattern = GetLedPattern();
+ const Common::Input::LedStatus status = {
+ .led_1 = pattern.position1 != 0,
+ .led_2 = pattern.position2 != 0,
+ .led_3 = pattern.position3 != 0,
+ .led_4 = pattern.position4 != 0,
+ };
+ device->SetLED(status);
+ }
+}
+
+void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
+ supported_style_tag = supported_styles;
+ if (!is_connected) {
+ return;
+ }
+ if (!IsControllerSupported()) {
+ LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
+ npad_type);
+ Disconnect();
+ }
+}
+
+bool EmulatedController::IsControllerSupported() const {
+ switch (npad_type) {
+ case NpadStyleIndex::ProController:
+ return supported_style_tag.fullkey;
+ case NpadStyleIndex::Handheld:
+ return supported_style_tag.handheld;
+ case NpadStyleIndex::JoyconDual:
+ return supported_style_tag.joycon_dual;
+ case NpadStyleIndex::JoyconLeft:
+ return supported_style_tag.joycon_left;
+ case NpadStyleIndex::JoyconRight:
+ return supported_style_tag.joycon_right;
+ case NpadStyleIndex::GameCube:
+ return supported_style_tag.gamecube;
+ case NpadStyleIndex::Pokeball:
+ return supported_style_tag.palma;
+ case NpadStyleIndex::NES:
+ return supported_style_tag.lark;
+ case NpadStyleIndex::SNES:
+ return supported_style_tag.lucia;
+ case NpadStyleIndex::N64:
+ return supported_style_tag.lagoon;
+ case NpadStyleIndex::SegaGenesis:
+ return supported_style_tag.lager;
+ default:
+ return false;
+ }
+}
+
+void EmulatedController::Connect() {
+ if (!IsControllerSupported()) {
+ LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
+ return;
+ }
+ {
+ std::lock_guard lock{mutex};
+ if (is_configuring) {
+ tmp_is_connected = true;
+ TriggerOnChange(ControllerTriggerType::Connected, false);
+ return;
+ }
+
+ if (is_connected) {
+ return;
+ }
+ is_connected = true;
+ }
+ TriggerOnChange(ControllerTriggerType::Connected, true);
+}
+
+void EmulatedController::Disconnect() {
+ {
+ std::lock_guard lock{mutex};
+ if (is_configuring) {
+ tmp_is_connected = false;
+ TriggerOnChange(ControllerTriggerType::Disconnected, false);
+ return;
+ }
+
+ if (!is_connected) {
+ return;
+ }
+ is_connected = false;
+ }
+ TriggerOnChange(ControllerTriggerType::Disconnected, true);
+}
+
+bool EmulatedController::IsConnected(bool get_temporary_value) const {
+ if (get_temporary_value && is_configuring) {
+ return tmp_is_connected;
+ }
+ return is_connected;
+}
+
+bool EmulatedController::IsVibrationEnabled() const {
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ const auto& player = Settings::values.players.GetValue()[player_index];
+ return player.vibration_enabled;
+}
+
+NpadIdType EmulatedController::GetNpadIdType() const {
+ return npad_id_type;
+}
+
+NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
+ if (get_temporary_value && is_configuring) {
+ return tmp_npad_type;
+ }
+ return npad_type;
+}
+
+void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
+ {
+ std::lock_guard lock{mutex};
+
+ if (is_configuring) {
+ if (tmp_npad_type == npad_type_) {
+ return;
+ }
+ tmp_npad_type = npad_type_;
+ TriggerOnChange(ControllerTriggerType::Type, false);
+ return;
+ }
+
+ if (npad_type == npad_type_) {
+ return;
+ }
+ if (is_connected) {
+ LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
+ NpadIdTypeToIndex(npad_id_type));
+ }
+ npad_type = npad_type_;
+ }
+ TriggerOnChange(ControllerTriggerType::Type, true);
+}
+
+LedPattern EmulatedController::GetLedPattern() const {
+ switch (npad_id_type) {
+ case NpadIdType::Player1:
+ return LedPattern{1, 0, 0, 0};
+ case NpadIdType::Player2:
+ return LedPattern{1, 1, 0, 0};
+ case NpadIdType::Player3:
+ return LedPattern{1, 1, 1, 0};
+ case NpadIdType::Player4:
+ return LedPattern{1, 1, 1, 1};
+ case NpadIdType::Player5:
+ return LedPattern{1, 0, 0, 1};
+ case NpadIdType::Player6:
+ return LedPattern{1, 0, 1, 0};
+ case NpadIdType::Player7:
+ return LedPattern{1, 0, 1, 1};
+ case NpadIdType::Player8:
+ return LedPattern{0, 1, 1, 0};
+ default:
+ return LedPattern{0, 0, 0, 0};
+ }
+}
+
+ButtonValues EmulatedController::GetButtonsValues() const {
+ return controller.button_values;
+}
+
+SticksValues EmulatedController::GetSticksValues() const {
+ return controller.stick_values;
+}
+
+TriggerValues EmulatedController::GetTriggersValues() const {
+ return controller.trigger_values;
+}
+
+ControllerMotionValues EmulatedController::GetMotionValues() const {
+ return controller.motion_values;
+}
+
+ColorValues EmulatedController::GetColorsValues() const {
+ return controller.color_values;
+}
+
+BatteryValues EmulatedController::GetBatteryValues() const {
+ return controller.battery_values;
+}
+
+NpadButtonState EmulatedController::GetNpadButtons() const {
+ if (is_configuring) {
+ return {};
+ }
+ return controller.npad_button_state;
+}
+
+DebugPadButton EmulatedController::GetDebugPadButtons() const {
+ if (is_configuring) {
+ return {};
+ }
+ return controller.debug_pad_button_state;
+}
+
+AnalogSticks EmulatedController::GetSticks() const {
+ if (is_configuring) {
+ return {};
+ }
+ // Some drivers like stick from buttons need constant refreshing
+ for (auto& device : stick_devices) {
+ if (!device) {
+ continue;
+ }
+ device->SoftUpdate();
+ }
+ return controller.analog_stick_state;
+}
+
+NpadGcTriggerState EmulatedController::GetTriggers() const {
+ if (is_configuring) {
+ return {};
+ }
+ return controller.gc_trigger_state;
+}
+
+MotionState EmulatedController::GetMotions() const {
+ if (force_update_motion) {
+ for (auto& device : motion_devices) {
+ if (!device) {
+ continue;
+ }
+ device->ForceUpdate();
+ }
+ }
+ return controller.motion_state;
+}
+
+ControllerColors EmulatedController::GetColors() const {
+ return controller.colors_state;
+}
+
+BatteryLevelState EmulatedController::GetBattery() const {
+ return controller.battery_state;
+}
+
+void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
+ for (const auto& poller_pair : callback_list) {
+ const ControllerUpdateCallback& poller = poller_pair.second;
+ if (!is_npad_service_update && poller.is_npad_service) {
+ continue;
+ }
+ if (poller.on_change) {
+ poller.on_change(type);
+ }
+ }
+}
+
+int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
+ std::lock_guard lock{mutex};
+ callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
+ return last_callback_key++;
+}
+
+void EmulatedController::DeleteCallback(int key) {
+ std::lock_guard lock{mutex};
+ const auto& iterator = callback_list.find(key);
+ if (iterator == callback_list.end()) {
+ LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
+ return;
+ }
+ callback_list.erase(iterator);
+}
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "common/input.h"
+#include "common/param_package.h"
+#include "common/point.h"
+#include "common/quaternion.h"
+#include "common/settings.h"
+#include "common/vector_math.h"
+#include "core/hid/hid_types.h"
+#include "core/hid/motion_input.h"
+
+namespace Core::HID {
+const std::size_t max_emulated_controllers = 2;
+struct ControllerMotionInfo {
+ Common::Input::MotionStatus raw_status{};
+ MotionInput emulated{};
+};
+
+using ButtonDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>;
+using StickDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
+using ControllerMotionDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
+using TriggerDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
+using BatteryDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
+using OutputDevices =
+ std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
+
+using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
+using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
+using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
+using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
+using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
+using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
+
+using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
+using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
+using TriggerValues =
+ std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
+using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
+using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
+using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
+using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
+
+struct AnalogSticks {
+ AnalogStickState left{};
+ AnalogStickState right{};
+};
+
+struct ControllerColors {
+ NpadControllerColor fullkey{};
+ NpadControllerColor left{};
+ NpadControllerColor right{};
+};
+
+struct BatteryLevelState {
+ NpadPowerInfo dual{};
+ NpadPowerInfo left{};
+ NpadPowerInfo right{};
+};
+
+struct ControllerMotion {
+ Common::Vec3f accel{};
+ Common::Vec3f gyro{};
+ Common::Vec3f rotation{};
+ std::array<Common::Vec3f, 3> orientation{};
+ bool is_at_rest{};
+};
+
+enum EmulatedDeviceIndex : u8 {
+ LeftIndex,
+ RightIndex,
+ DualIndex,
+ AllDevices,
+};
+
+using MotionState = std::array<ControllerMotion, 2>;
+
+struct ControllerStatus {
+ // Data from input_common
+ ButtonValues button_values{};
+ SticksValues stick_values{};
+ ControllerMotionValues motion_values{};
+ TriggerValues trigger_values{};
+ ColorValues color_values{};
+ BatteryValues battery_values{};
+ VibrationValues vibration_values{};
+
+ // Data for HID serices
+ NpadButtonState npad_button_state{};
+ DebugPadButton debug_pad_button_state{};
+ AnalogSticks analog_stick_state{};
+ MotionState motion_state{};
+ NpadGcTriggerState gc_trigger_state{};
+ ControllerColors colors_state{};
+ BatteryLevelState battery_state{};
+};
+
+enum class ControllerTriggerType {
+ Button,
+ Stick,
+ Trigger,
+ Motion,
+ Color,
+ Battery,
+ Vibration,
+ Connected,
+ Disconnected,
+ Type,
+ All,
+};
+
+struct ControllerUpdateCallback {
+ std::function<void(ControllerTriggerType)> on_change;
+ bool is_npad_service;
+};
+
+class EmulatedController {
+public:
+ /**
+ * Contains all input data (buttons, joysticks, vibration, and motion) within this controller.
+ * @param npad_id_type npad id type for this specific controller
+ */
+ explicit EmulatedController(NpadIdType npad_id_type_);
+ ~EmulatedController();
+
+ YUZU_NON_COPYABLE(EmulatedController);
+ YUZU_NON_MOVEABLE(EmulatedController);
+
+ /// Converts the controller type from settings to npad type
+ static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type);
+
+ /// Converts npad type to the equivalent of controller type from settings
+ static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type);
+
+ /// Gets the NpadIdType for this controller
+ NpadIdType GetNpadIdType() const;
+
+ /// Sets the NpadStyleIndex for this controller
+ void SetNpadStyleIndex(NpadStyleIndex npad_type_);
+
+ /**
+ * Gets the NpadStyleIndex for this controller
+ * @param get_temporary_value If true tmp_npad_type will be returned
+ * @return NpadStyleIndex set on the controller
+ */
+ NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
+
+ /**
+ * Sets the supported controller types. Disconnects the controller if current type is not
+ * supported
+ * @param supported_styles bitflag with supported types
+ */
+ void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
+
+ /// Sets the connected status to true
+ void Connect();
+
+ /// Sets the connected status to false
+ void Disconnect();
+
+ /**
+ * Is the emulated connected
+ * @param get_temporary_value If true tmp_is_connected will be returned
+ * @return true if the controller has the connected status
+ */
+ bool IsConnected(bool get_temporary_value = false) const;
+
+ /// Returns true if vibration is enabled
+ bool IsVibrationEnabled() const;
+
+ /// Removes all callbacks created from input devices
+ void UnloadInput();
+
+ /**
+ * Sets the emulated controller into configuring mode
+ * This prevents the modification of the HID state of the emulated controller by input commands
+ */
+ void EnableConfiguration();
+
+ /// Returns the emulated controller into normal mode, allowing the modification of the HID state
+ void DisableConfiguration();
+
+ /// Returns true if the emulated controller is in configuring mode
+ bool IsConfiguring() const;
+
+ /// Reload all input devices
+ void ReloadInput();
+
+ /// Overrides current mapped devices with the stored configuration and reloads all input devices
+ void ReloadFromSettings();
+
+ /// Saves the current mapped configuration
+ void SaveCurrentConfig();
+
+ /// Reverts any mapped changes made that weren't saved
+ void RestoreConfig();
+
+ /// Returns a vector of mapped devices from the mapped button and stick parameters
+ std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const;
+
+ // Returns the current mapped button device
+ Common::ParamPackage GetButtonParam(std::size_t index) const;
+
+ // Returns the current mapped stick device
+ Common::ParamPackage GetStickParam(std::size_t index) const;
+
+ // Returns the current mapped motion device
+ Common::ParamPackage GetMotionParam(std::size_t index) const;
+
+ /**
+ * Updates the current mapped button device
+ * @param param ParamPackage with controller data to be mapped
+ */
+ void SetButtonParam(std::size_t index, Common::ParamPackage param);
+
+ /**
+ * Updates the current mapped stick device
+ * @param param ParamPackage with controller data to be mapped
+ */
+ void SetStickParam(std::size_t index, Common::ParamPackage param);
+
+ /**
+ * Updates the current mapped motion device
+ * @param param ParamPackage with controller data to be mapped
+ */
+ void SetMotionParam(std::size_t index, Common::ParamPackage param);
+
+ /// Returns the latest button status from the controller with parameters
+ ButtonValues GetButtonsValues() const;
+
+ /// Returns the latest analog stick status from the controller with parameters
+ SticksValues GetSticksValues() const;
+
+ /// Returns the latest trigger status from the controller with parameters
+ TriggerValues GetTriggersValues() const;
+
+ /// Returns the latest motion status from the controller with parameters
+ ControllerMotionValues GetMotionValues() const;
+
+ /// Returns the latest color status from the controller with parameters
+ ColorValues GetColorsValues() const;
+
+ /// Returns the latest battery status from the controller with parameters
+ BatteryValues GetBatteryValues() const;
+
+ /// Returns the latest status of button input for the npad service
+ NpadButtonState GetNpadButtons() const;
+
+ /// Returns the latest status of button input for the debug pad service
+ DebugPadButton GetDebugPadButtons() const;
+
+ /// Returns the latest status of stick input from the mouse
+ AnalogSticks GetSticks() const;
+
+ /// Returns the latest status of trigger input from the mouse
+ NpadGcTriggerState GetTriggers() const;
+
+ /// Returns the latest status of motion input from the mouse
+ MotionState GetMotions() const;
+
+ /// Returns the latest color value from the controller
+ ControllerColors GetColors() const;
+
+ /// Returns the latest battery status from the controller
+ BatteryLevelState GetBattery() const;
+
+ /**
+ * Sends a specific vibration to the output device
+ * @return returns true if vibration had no errors
+ */
+ bool SetVibration(std::size_t device_index, VibrationValue vibration);
+
+ /**
+ * Sends a small vibration to the output device
+ * @return returns true if SetVibration was successfull
+ */
+ bool TestVibration(std::size_t device_index);
+
+ /// Returns the led pattern corresponding to this emulated controller
+ LedPattern GetLedPattern() const;
+
+ /// Asks the output device to change the player led pattern
+ void SetLedPattern();
+
+ /**
+ * Adds a callback to the list of events
+ * @param update_callback A ConsoleUpdateCallback that will be triggered
+ * @return an unique key corresponding to the callback index in the list
+ */
+ int SetCallback(ControllerUpdateCallback update_callback);
+
+ /**
+ * Removes a callback from the list stopping any future events to this object
+ * @param key Key corresponding to the callback index in the list
+ */
+ void DeleteCallback(int key);
+
+private:
+ /// creates input devices from params
+ void LoadDevices();
+
+ /// Set the params for TAS devices
+ void LoadTASParams();
+
+ /**
+ * Checks the current controller type against the supported_style_tag
+ * @return true if the controller is supported
+ */
+ bool IsControllerSupported() const;
+
+ /**
+ * Updates the button status of the controller
+ * @param callback A CallbackStatus containing the button status
+ * @param index Button ID of the to be updated
+ */
+ void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
+ Common::UUID uuid);
+
+ /**
+ * Updates the analog stick status of the controller
+ * @param callback A CallbackStatus containing the analog stick status
+ * @param index stick ID of the to be updated
+ */
+ void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
+ Common::UUID uuid);
+
+ /**
+ * Updates the trigger status of the controller
+ * @param callback A CallbackStatus containing the trigger status
+ * @param index trigger ID of the to be updated
+ */
+ void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
+ Common::UUID uuid);
+
+ /**
+ * Updates the motion status of the controller
+ * @param callback A CallbackStatus containing gyro and accelerometer data
+ * @param index motion ID of the to be updated
+ */
+ void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
+
+ /**
+ * Updates the battery status of the controller
+ * @param callback A CallbackStatus containing the battery status
+ * @param index Button ID of the to be updated
+ */
+ void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
+
+ /**
+ * Triggers a callback that something has changed on the controller status
+ * @param type Input type of the event to trigger
+ * @param is_service_update indicates if this event should only be sent to HID services
+ */
+ void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
+
+ NpadIdType npad_id_type;
+ NpadStyleIndex npad_type{NpadStyleIndex::None};
+ NpadStyleTag supported_style_tag{NpadStyleSet::All};
+ bool is_connected{false};
+ bool is_configuring{false};
+ f32 motion_sensitivity{0.01f};
+ bool force_update_motion{false};
+
+ // Temporary values to avoid doing changes while the controller is in configuring mode
+ NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
+ bool tmp_is_connected{false};
+
+ ButtonParams button_params;
+ StickParams stick_params;
+ ControllerMotionParams motion_params;
+ TriggerParams trigger_params;
+ BatteryParams battery_params;
+ OutputParams output_params;
+
+ ButtonDevices button_devices;
+ StickDevices stick_devices;
+ ControllerMotionDevices motion_devices;
+ TriggerDevices trigger_devices;
+ BatteryDevices battery_devices;
+ OutputDevices output_devices;
+
+ // TAS related variables
+ ButtonParams tas_button_params;
+ StickParams tas_stick_params;
+ ButtonDevices tas_button_devices;
+ StickDevices tas_stick_devices;
+
+ mutable std::mutex mutex;
+ std::unordered_map<int, ControllerUpdateCallback> callback_list;
+ int last_callback_key = 0;
+
+ // Stores the current status of all controller input
+ ControllerStatus controller;
+};
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include <algorithm>
+#include <fmt/format.h>
+
+#include "core/hid/emulated_devices.h"
+#include "core/hid/input_converter.h"
+
+namespace Core::HID {
+
+EmulatedDevices::EmulatedDevices() = default;
+
+EmulatedDevices::~EmulatedDevices() = default;
+
+void EmulatedDevices::ReloadFromSettings() {
+ ReloadInput();
+}
+
+void EmulatedDevices::ReloadInput() {
+ // If you load any device here add the equivalent to the UnloadInput() function
+ std::size_t key_index = 0;
+ for (auto& mouse_device : mouse_button_devices) {
+ Common::ParamPackage mouse_params;
+ mouse_params.Set("engine", "mouse");
+ mouse_params.Set("button", static_cast<int>(key_index));
+ mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
+ key_index++;
+ }
+
+ mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
+ "engine:mouse,axis_x:0,axis_y:1");
+
+ // First two axis are reserved for mouse position
+ key_index = 2;
+ for (auto& mouse_device : mouse_analog_devices) {
+ Common::ParamPackage mouse_params;
+ mouse_params.Set("engine", "mouse");
+ mouse_params.Set("axis", static_cast<int>(key_index));
+ mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
+ key_index++;
+ }
+
+ key_index = 0;
+ for (auto& keyboard_device : keyboard_devices) {
+ // Keyboard keys are only mapped on port 1, pad 0
+ Common::ParamPackage keyboard_params;
+ keyboard_params.Set("engine", "keyboard");
+ keyboard_params.Set("button", static_cast<int>(key_index));
+ keyboard_params.Set("port", 1);
+ keyboard_params.Set("pad", 0);
+ keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
+ key_index++;
+ }
+
+ key_index = 0;
+ for (auto& keyboard_device : keyboard_modifier_devices) {
+ // Keyboard moddifiers are only mapped on port 1, pad 1
+ Common::ParamPackage keyboard_params;
+ keyboard_params.Set("engine", "keyboard");
+ keyboard_params.Set("button", static_cast<int>(key_index));
+ keyboard_params.Set("port", 1);
+ keyboard_params.Set("pad", 1);
+ keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
+ key_index++;
+ }
+
+ for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
+ if (!mouse_button_devices[index]) {
+ continue;
+ }
+ mouse_button_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetMouseButton(callback, index);
+ },
+ });
+ }
+
+ for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) {
+ if (!mouse_analog_devices[index]) {
+ continue;
+ }
+ mouse_analog_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetMouseAnalog(callback, index);
+ },
+ });
+ }
+
+ if (mouse_stick_device) {
+ mouse_stick_device->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetMouseStick(callback); },
+ });
+ }
+
+ for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
+ if (!keyboard_devices[index]) {
+ continue;
+ }
+ keyboard_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetKeyboardButton(callback, index);
+ },
+ });
+ }
+
+ for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
+ if (!keyboard_modifier_devices[index]) {
+ continue;
+ }
+ keyboard_modifier_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetKeyboardModifier(callback, index);
+ },
+ });
+ }
+}
+
+void EmulatedDevices::UnloadInput() {
+ for (auto& button : mouse_button_devices) {
+ button.reset();
+ }
+ for (auto& analog : mouse_analog_devices) {
+ analog.reset();
+ }
+ mouse_stick_device.reset();
+ for (auto& button : keyboard_devices) {
+ button.reset();
+ }
+ for (auto& button : keyboard_modifier_devices) {
+ button.reset();
+ }
+}
+
+void EmulatedDevices::EnableConfiguration() {
+ is_configuring = true;
+ SaveCurrentConfig();
+}
+
+void EmulatedDevices::DisableConfiguration() {
+ is_configuring = false;
+}
+
+bool EmulatedDevices::IsConfiguring() const {
+ return is_configuring;
+}
+
+void EmulatedDevices::SaveCurrentConfig() {
+ if (!is_configuring) {
+ return;
+ }
+}
+
+void EmulatedDevices::RestoreConfig() {
+ if (!is_configuring) {
+ return;
+ }
+ ReloadFromSettings();
+}
+
+void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
+ std::size_t index) {
+ if (index >= device_status.keyboard_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = device_status.keyboard_values[index];
+ current_status.toggle = new_status.toggle;
+
+ // Update button status with current status
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
+ }
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
+ }
+
+ // Unlock button, ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
+ }
+ }
+
+ if (!value_changed) {
+ return;
+ }
+
+ if (is_configuring) {
+ TriggerOnChange(DeviceTriggerType::Keyboard);
+ return;
+ }
+
+ // Index should be converted from NativeKeyboard to KeyboardKeyIndex
+ UpdateKey(index, current_status.value);
+
+ TriggerOnChange(DeviceTriggerType::Keyboard);
+}
+
+void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
+ constexpr std::size_t KEYS_PER_BYTE = 8;
+ auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE];
+ const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE));
+ if (status) {
+ entry = entry | mask;
+ } else {
+ entry = static_cast<u8>(entry & ~mask);
+ }
+}
+
+void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
+ std::size_t index) {
+ if (index >= device_status.keyboard_moddifier_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = device_status.keyboard_moddifier_values[index];
+ current_status.toggle = new_status.toggle;
+
+ // Update button status with current
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
+ }
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
+ }
+
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
+ }
+ }
+
+ if (!value_changed) {
+ return;
+ }
+
+ if (is_configuring) {
+ TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
+ return;
+ }
+
+ switch (index) {
+ case Settings::NativeKeyboard::LeftControl:
+ case Settings::NativeKeyboard::RightControl:
+ device_status.keyboard_moddifier_state.control.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::LeftShift:
+ case Settings::NativeKeyboard::RightShift:
+ device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::LeftAlt:
+ device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::RightAlt:
+ device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::CapsLock:
+ device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::ScrollLock:
+ device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::NumLock:
+ device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
+ break;
+ }
+
+ TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
+}
+
+void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
+ std::size_t index) {
+ if (index >= device_status.mouse_button_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = device_status.mouse_button_values[index];
+ current_status.toggle = new_status.toggle;
+
+ // Update button status with current
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
+ }
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
+ }
+
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
+ }
+ }
+
+ if (!value_changed) {
+ return;
+ }
+
+ if (is_configuring) {
+ TriggerOnChange(DeviceTriggerType::Mouse);
+ return;
+ }
+
+ switch (index) {
+ case Settings::NativeMouseButton::Left:
+ device_status.mouse_button_state.left.Assign(current_status.value);
+ break;
+ case Settings::NativeMouseButton::Right:
+ device_status.mouse_button_state.right.Assign(current_status.value);
+ break;
+ case Settings::NativeMouseButton::Middle:
+ device_status.mouse_button_state.middle.Assign(current_status.value);
+ break;
+ case Settings::NativeMouseButton::Forward:
+ device_status.mouse_button_state.forward.Assign(current_status.value);
+ break;
+ case Settings::NativeMouseButton::Back:
+ device_status.mouse_button_state.back.Assign(current_status.value);
+ break;
+ }
+
+ TriggerOnChange(DeviceTriggerType::Mouse);
+}
+
+void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callback,
+ std::size_t index) {
+ if (index >= device_status.mouse_analog_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ const auto analog_value = TransformToAnalog(callback);
+
+ device_status.mouse_analog_values[index] = analog_value;
+
+ if (is_configuring) {
+ device_status.mouse_position_state = {};
+ TriggerOnChange(DeviceTriggerType::Mouse);
+ return;
+ }
+
+ switch (index) {
+ case Settings::NativeMouseWheel::X:
+ device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value);
+ break;
+ case Settings::NativeMouseWheel::Y:
+ device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value);
+ break;
+ }
+
+ TriggerOnChange(DeviceTriggerType::Mouse);
+}
+
+void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
+ std::lock_guard lock{mutex};
+ const auto touch_value = TransformToTouch(callback);
+
+ device_status.mouse_stick_value = touch_value;
+
+ if (is_configuring) {
+ device_status.mouse_position_state = {};
+ TriggerOnChange(DeviceTriggerType::Mouse);
+ return;
+ }
+
+ device_status.mouse_position_state.x = touch_value.x.value;
+ device_status.mouse_position_state.y = touch_value.y.value;
+
+ TriggerOnChange(DeviceTriggerType::Mouse);
+}
+
+KeyboardValues EmulatedDevices::GetKeyboardValues() const {
+ return device_status.keyboard_values;
+}
+
+KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
+ return device_status.keyboard_moddifier_values;
+}
+
+MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
+ return device_status.mouse_button_values;
+}
+
+KeyboardKey EmulatedDevices::GetKeyboard() const {
+ return device_status.keyboard_state;
+}
+
+KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
+ return device_status.keyboard_moddifier_state;
+}
+
+MouseButton EmulatedDevices::GetMouseButtons() const {
+ return device_status.mouse_button_state;
+}
+
+MousePosition EmulatedDevices::GetMousePosition() const {
+ return device_status.mouse_position_state;
+}
+
+AnalogStickState EmulatedDevices::GetMouseWheel() const {
+ return device_status.mouse_wheel_state;
+}
+
+void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
+ for (const auto& poller_pair : callback_list) {
+ const InterfaceUpdateCallback& poller = poller_pair.second;
+ if (poller.on_change) {
+ poller.on_change(type);
+ }
+ }
+}
+
+int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
+ std::lock_guard lock{mutex};
+ callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
+ return last_callback_key++;
+}
+
+void EmulatedDevices::DeleteCallback(int key) {
+ std::lock_guard lock{mutex};
+ const auto& iterator = callback_list.find(key);
+ if (iterator == callback_list.end()) {
+ LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
+ return;
+ }
+ callback_list.erase(iterator);
+}
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "common/input.h"
+#include "common/param_package.h"
+#include "common/settings.h"
+#include "core/hid/hid_types.h"
+
+namespace Core::HID {
+using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
+ Settings::NativeKeyboard::NumKeyboardKeys>;
+using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
+ Settings::NativeKeyboard::NumKeyboardMods>;
+using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
+ Settings::NativeMouseButton::NumMouseButtons>;
+using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
+ Settings::NativeMouseWheel::NumMouseWheels>;
+using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
+
+using MouseButtonParams =
+ std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
+
+using KeyboardValues =
+ std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
+using KeyboardModifierValues =
+ std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
+using MouseButtonValues =
+ std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
+using MouseAnalogValues =
+ std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
+using MouseStickValue = Common::Input::TouchStatus;
+
+struct MousePosition {
+ f32 x;
+ f32 y;
+};
+
+struct DeviceStatus {
+ // Data from input_common
+ KeyboardValues keyboard_values{};
+ KeyboardModifierValues keyboard_moddifier_values{};
+ MouseButtonValues mouse_button_values{};
+ MouseAnalogValues mouse_analog_values{};
+ MouseStickValue mouse_stick_value{};
+
+ // Data for HID serices
+ KeyboardKey keyboard_state{};
+ KeyboardModifier keyboard_moddifier_state{};
+ MouseButton mouse_button_state{};
+ MousePosition mouse_position_state{};
+ AnalogStickState mouse_wheel_state{};
+};
+
+enum class DeviceTriggerType {
+ Keyboard,
+ KeyboardModdifier,
+ Mouse,
+};
+
+struct InterfaceUpdateCallback {
+ std::function<void(DeviceTriggerType)> on_change;
+};
+
+class EmulatedDevices {
+public:
+ /**
+ * Contains all input data related to external devices that aren't necesarily a controller
+ * This includes devices such as the keyboard or mouse
+ */
+ explicit EmulatedDevices();
+ ~EmulatedDevices();
+
+ YUZU_NON_COPYABLE(EmulatedDevices);
+ YUZU_NON_MOVEABLE(EmulatedDevices);
+
+ /// Removes all callbacks created from input devices
+ void UnloadInput();
+
+ /**
+ * Sets the emulated devices into configuring mode
+ * This prevents the modification of the HID state of the emulated devices by input commands
+ */
+ void EnableConfiguration();
+
+ /// Returns the emulated devices into normal mode, allowing the modification of the HID state
+ void DisableConfiguration();
+
+ /// Returns true if the emulated device is in configuring mode
+ bool IsConfiguring() const;
+
+ /// Reload all input devices
+ void ReloadInput();
+
+ /// Overrides current mapped devices with the stored configuration and reloads all input devices
+ void ReloadFromSettings();
+
+ /// Saves the current mapped configuration
+ void SaveCurrentConfig();
+
+ /// Reverts any mapped changes made that weren't saved
+ void RestoreConfig();
+
+ /// Returns the latest status of button input from the keyboard with parameters
+ KeyboardValues GetKeyboardValues() const;
+
+ /// Returns the latest status of button input from the keyboard modifiers with parameters
+ KeyboardModifierValues GetKeyboardModdifierValues() const;
+
+ /// Returns the latest status of button input from the mouse with parameters
+ MouseButtonValues GetMouseButtonsValues() const;
+
+ /// Returns the latest status of button input from the keyboard
+ KeyboardKey GetKeyboard() const;
+
+ /// Returns the latest status of button input from the keyboard modifiers
+ KeyboardModifier GetKeyboardModifier() const;
+
+ /// Returns the latest status of button input from the mouse
+ MouseButton GetMouseButtons() const;
+
+ /// Returns the latest mouse coordinates
+ MousePosition GetMousePosition() const;
+
+ /// Returns the latest mouse wheel change
+ AnalogStickState GetMouseWheel() const;
+
+ /**
+ * Adds a callback to the list of events
+ * @param update_callback InterfaceUpdateCallback that will be triggered
+ * @return an unique key corresponding to the callback index in the list
+ */
+ int SetCallback(InterfaceUpdateCallback update_callback);
+
+ /**
+ * Removes a callback from the list stopping any future events to this object
+ * @param key Key corresponding to the callback index in the list
+ */
+ void DeleteCallback(int key);
+
+private:
+ /// Helps assigning a value to keyboard_state
+ void UpdateKey(std::size_t key_index, bool status);
+
+ /**
+ * Updates the touch status of the keyboard device
+ * @param callback A CallbackStatus containing the key status
+ * @param index key ID to be updated
+ */
+ void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);
+
+ /**
+ * Updates the keyboard status of the keyboard device
+ * @param callback A CallbackStatus containing the modifier key status
+ * @param index modifier key ID to be updated
+ */
+ void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);
+
+ /**
+ * Updates the mouse button status of the mouse device
+ * @param callback A CallbackStatus containing the button status
+ * @param index Button ID to be updated
+ */
+ void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);
+
+ /**
+ * Updates the mouse wheel status of the mouse device
+ * @param callback A CallbackStatus containing the wheel status
+ * @param index wheel ID to be updated
+ */
+ void SetMouseAnalog(const Common::Input::CallbackStatus& callback, std::size_t index);
+
+ /**
+ * Updates the mouse position status of the mouse device
+ * @param callback A CallbackStatus containing the position status
+ */
+ void SetMouseStick(const Common::Input::CallbackStatus& callback);
+
+ /**
+ * Triggers a callback that something has changed on the device status
+ * @param type Input type of the event to trigger
+ */
+ void TriggerOnChange(DeviceTriggerType type);
+
+ bool is_configuring{false};
+
+ KeyboardDevices keyboard_devices;
+ KeyboardModifierDevices keyboard_modifier_devices;
+ MouseButtonDevices mouse_button_devices;
+ MouseAnalogDevices mouse_analog_devices;
+ MouseStickDevice mouse_stick_device;
+
+ mutable std::mutex mutex;
+ std::unordered_map<int, InterfaceUpdateCallback> callback_list;
+ int last_callback_key = 0;
+
+ // Stores the current status of all external device input
+ DeviceStatus device_status;
+};
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/hid/emulated_console.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/emulated_devices.h"
+#include "core/hid/hid_core.h"
+
+namespace Core::HID {
+
+HIDCore::HIDCore()
+ : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
+ player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
+ player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
+ player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
+ player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
+ player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
+ player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
+ player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
+ other{std::make_unique<EmulatedController>(NpadIdType::Other)},
+ handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
+ console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
+
+HIDCore::~HIDCore() = default;
+
+EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
+ switch (npad_id_type) {
+ case NpadIdType::Player1:
+ return player_1.get();
+ case NpadIdType::Player2:
+ return player_2.get();
+ case NpadIdType::Player3:
+ return player_3.get();
+ case NpadIdType::Player4:
+ return player_4.get();
+ case NpadIdType::Player5:
+ return player_5.get();
+ case NpadIdType::Player6:
+ return player_6.get();
+ case NpadIdType::Player7:
+ return player_7.get();
+ case NpadIdType::Player8:
+ return player_8.get();
+ case NpadIdType::Other:
+ return other.get();
+ case NpadIdType::Handheld:
+ return handheld.get();
+ case NpadIdType::Invalid:
+ default:
+ UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
+ return nullptr;
+ }
+}
+
+const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
+ switch (npad_id_type) {
+ case NpadIdType::Player1:
+ return player_1.get();
+ case NpadIdType::Player2:
+ return player_2.get();
+ case NpadIdType::Player3:
+ return player_3.get();
+ case NpadIdType::Player4:
+ return player_4.get();
+ case NpadIdType::Player5:
+ return player_5.get();
+ case NpadIdType::Player6:
+ return player_6.get();
+ case NpadIdType::Player7:
+ return player_7.get();
+ case NpadIdType::Player8:
+ return player_8.get();
+ case NpadIdType::Other:
+ return other.get();
+ case NpadIdType::Handheld:
+ return handheld.get();
+ case NpadIdType::Invalid:
+ default:
+ UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
+ return nullptr;
+ }
+}
+EmulatedConsole* HIDCore::GetEmulatedConsole() {
+ return console.get();
+}
+
+const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
+ return console.get();
+}
+
+EmulatedDevices* HIDCore::GetEmulatedDevices() {
+ return devices.get();
+}
+
+const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
+ return devices.get();
+}
+
+EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
+ return GetEmulatedController(IndexToNpadIdType(index));
+}
+
+const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
+ return GetEmulatedController(IndexToNpadIdType(index));
+}
+
+void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
+ supported_style_tag.raw = style_tag.raw;
+ player_1->SetSupportedNpadStyleTag(supported_style_tag);
+ player_2->SetSupportedNpadStyleTag(supported_style_tag);
+ player_3->SetSupportedNpadStyleTag(supported_style_tag);
+ player_4->SetSupportedNpadStyleTag(supported_style_tag);
+ player_5->SetSupportedNpadStyleTag(supported_style_tag);
+ player_6->SetSupportedNpadStyleTag(supported_style_tag);
+ player_7->SetSupportedNpadStyleTag(supported_style_tag);
+ player_8->SetSupportedNpadStyleTag(supported_style_tag);
+ other->SetSupportedNpadStyleTag(supported_style_tag);
+ handheld->SetSupportedNpadStyleTag(supported_style_tag);
+}
+
+NpadStyleTag HIDCore::GetSupportedStyleTag() const {
+ return supported_style_tag;
+}
+
+s8 HIDCore::GetPlayerCount() const {
+ s8 active_players = 0;
+ for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) {
+ const auto* const controller = GetEmulatedControllerByIndex(player_index);
+ if (controller->IsConnected()) {
+ active_players++;
+ }
+ }
+ return active_players;
+}
+
+NpadIdType HIDCore::GetFirstNpadId() const {
+ for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
+ const auto* const controller = GetEmulatedControllerByIndex(player_index);
+ if (controller->IsConnected()) {
+ return controller->GetNpadIdType();
+ }
+ }
+ return NpadIdType::Player1;
+}
+
+NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
+ for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
+ const auto* const controller = GetEmulatedControllerByIndex(player_index);
+ if (!controller->IsConnected()) {
+ return controller->GetNpadIdType();
+ }
+ }
+ return NpadIdType::Player1;
+}
+
+void HIDCore::EnableAllControllerConfiguration() {
+ player_1->EnableConfiguration();
+ player_2->EnableConfiguration();
+ player_3->EnableConfiguration();
+ player_4->EnableConfiguration();
+ player_5->EnableConfiguration();
+ player_6->EnableConfiguration();
+ player_7->EnableConfiguration();
+ player_8->EnableConfiguration();
+ other->EnableConfiguration();
+ handheld->EnableConfiguration();
+}
+
+void HIDCore::DisableAllControllerConfiguration() {
+ player_1->DisableConfiguration();
+ player_2->DisableConfiguration();
+ player_3->DisableConfiguration();
+ player_4->DisableConfiguration();
+ player_5->DisableConfiguration();
+ player_6->DisableConfiguration();
+ player_7->DisableConfiguration();
+ player_8->DisableConfiguration();
+ other->DisableConfiguration();
+ handheld->DisableConfiguration();
+}
+
+void HIDCore::ReloadInputDevices() {
+ player_1->ReloadFromSettings();
+ player_2->ReloadFromSettings();
+ player_3->ReloadFromSettings();
+ player_4->ReloadFromSettings();
+ player_5->ReloadFromSettings();
+ player_6->ReloadFromSettings();
+ player_7->ReloadFromSettings();
+ player_8->ReloadFromSettings();
+ other->ReloadFromSettings();
+ handheld->ReloadFromSettings();
+ console->ReloadFromSettings();
+ devices->ReloadFromSettings();
+}
+
+void HIDCore::UnloadInputDevices() {
+ player_1->UnloadInput();
+ player_2->UnloadInput();
+ player_3->UnloadInput();
+ player_4->UnloadInput();
+ player_5->UnloadInput();
+ player_6->UnloadInput();
+ player_7->UnloadInput();
+ player_8->UnloadInput();
+ other->UnloadInput();
+ handheld->UnloadInput();
+ console->UnloadInput();
+ devices->UnloadInput();
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "core/hid/hid_types.h"
+
+namespace Core::HID {
+class EmulatedConsole;
+class EmulatedController;
+class EmulatedDevices;
+} // namespace Core::HID
+
+namespace Core::HID {
+
+class HIDCore {
+public:
+ explicit HIDCore();
+ ~HIDCore();
+
+ YUZU_NON_COPYABLE(HIDCore);
+ YUZU_NON_MOVEABLE(HIDCore);
+
+ EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
+ const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;
+
+ EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
+ const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
+
+ EmulatedConsole* GetEmulatedConsole();
+ const EmulatedConsole* GetEmulatedConsole() const;
+
+ EmulatedDevices* GetEmulatedDevices();
+ const EmulatedDevices* GetEmulatedDevices() const;
+
+ void SetSupportedStyleTag(NpadStyleTag style_tag);
+ NpadStyleTag GetSupportedStyleTag() const;
+
+ /// Counts the connected players from P1-P8
+ s8 GetPlayerCount() const;
+
+ /// Returns the first connected npad id
+ NpadIdType GetFirstNpadId() const;
+
+ /// Returns the first disconnected npad id
+ NpadIdType GetFirstDisconnectedNpadId() const;
+
+ /// Sets all emulated controllers into configuring mode.
+ void EnableAllControllerConfiguration();
+
+ /// Sets all emulated controllers into normal mode.
+ void DisableAllControllerConfiguration();
+
+ /// Reloads all input devices from settings
+ void ReloadInputDevices();
+
+ /// Removes all callbacks from input common
+ void UnloadInputDevices();
+
+ /// Number of emulated controllers
+ static constexpr std::size_t available_controllers{10};
+
+private:
+ std::unique_ptr<EmulatedController> player_1;
+ std::unique_ptr<EmulatedController> player_2;
+ std::unique_ptr<EmulatedController> player_3;
+ std::unique_ptr<EmulatedController> player_4;
+ std::unique_ptr<EmulatedController> player_5;
+ std::unique_ptr<EmulatedController> player_6;
+ std::unique_ptr<EmulatedController> player_7;
+ std::unique_ptr<EmulatedController> player_8;
+ std::unique_ptr<EmulatedController> other;
+ std::unique_ptr<EmulatedController> handheld;
+ std::unique_ptr<EmulatedConsole> console;
+ std::unique_ptr<EmulatedDevices> devices;
+ NpadStyleTag supported_style_tag{NpadStyleSet::All};
+};
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/point.h"
+#include "common/uuid.h"
+
+namespace Core::HID {
+
+enum class DeviceIndex : u8 {
+ Left = 0,
+ Right = 1,
+ None = 2,
+ MaxDeviceIndex = 3,
+};
+
+// This is nn::hid::NpadButton
+enum class NpadButton : u64 {
+ None = 0,
+ A = 1U << 0,
+ B = 1U << 1,
+ X = 1U << 2,
+ Y = 1U << 3,
+ StickL = 1U << 4,
+ StickR = 1U << 5,
+ L = 1U << 6,
+ R = 1U << 7,
+ ZL = 1U << 8,
+ ZR = 1U << 9,
+ Plus = 1U << 10,
+ Minus = 1U << 11,
+
+ Left = 1U << 12,
+ Up = 1U << 13,
+ Right = 1U << 14,
+ Down = 1U << 15,
+
+ StickLLeft = 1U << 16,
+ StickLUp = 1U << 17,
+ StickLRight = 1U << 18,
+ StickLDown = 1U << 19,
+
+ StickRLeft = 1U << 20,
+ StickRUp = 1U << 21,
+ StickRRight = 1U << 22,
+ StickRDown = 1U << 23,
+
+ LeftSL = 1U << 24,
+ LeftSR = 1U << 25,
+
+ RightSL = 1U << 26,
+ RightSR = 1U << 27,
+
+ Palma = 1U << 28,
+ Verification = 1U << 29,
+ HandheldLeftB = 1U << 30,
+ LagonCLeft = 1U << 31,
+ LagonCUp = 1ULL << 32,
+ LagonCRight = 1ULL << 33,
+ LagonCDown = 1ULL << 34,
+
+ All = 0xFFFFFFFFFFFFFFFFULL,
+};
+DECLARE_ENUM_FLAG_OPERATORS(NpadButton);
+
+enum class KeyboardKeyIndex : u32 {
+ A = 4,
+ B = 5,
+ C = 6,
+ D = 7,
+ E = 8,
+ F = 9,
+ G = 10,
+ H = 11,
+ I = 12,
+ J = 13,
+ K = 14,
+ L = 15,
+ M = 16,
+ N = 17,
+ O = 18,
+ P = 19,
+ Q = 20,
+ R = 21,
+ S = 22,
+ T = 23,
+ U = 24,
+ V = 25,
+ W = 26,
+ X = 27,
+ Y = 28,
+ Z = 29,
+ D1 = 30,
+ D2 = 31,
+ D3 = 32,
+ D4 = 33,
+ D5 = 34,
+ D6 = 35,
+ D7 = 36,
+ D8 = 37,
+ D9 = 38,
+ D0 = 39,
+ Return = 40,
+ Escape = 41,
+ Backspace = 42,
+ Tab = 43,
+ Space = 44,
+ Minus = 45,
+ Plus = 46,
+ OpenBracket = 47,
+ CloseBracket = 48,
+ Pipe = 49,
+ Tilde = 50,
+ Semicolon = 51,
+ Quote = 52,
+ Backquote = 53,
+ Comma = 54,
+ Period = 55,
+ Slash = 56,
+ CapsLock = 57,
+ F1 = 58,
+ F2 = 59,
+ F3 = 60,
+ F4 = 61,
+ F5 = 62,
+ F6 = 63,
+ F7 = 64,
+ F8 = 65,
+ F9 = 66,
+ F10 = 67,
+ F11 = 68,
+ F12 = 69,
+ PrintScreen = 70,
+ ScrollLock = 71,
+ Pause = 72,
+ Insert = 73,
+ Home = 74,
+ PageUp = 75,
+ Delete = 76,
+ End = 77,
+ PageDown = 78,
+ RightArrow = 79,
+ LeftArrow = 80,
+ DownArrow = 81,
+ UpArrow = 82,
+ NumLock = 83,
+ NumPadDivide = 84,
+ NumPadMultiply = 85,
+ NumPadSubtract = 86,
+ NumPadAdd = 87,
+ NumPadEnter = 88,
+ NumPad1 = 89,
+ NumPad2 = 90,
+ NumPad3 = 91,
+ NumPad4 = 92,
+ NumPad5 = 93,
+ NumPad6 = 94,
+ NumPad7 = 95,
+ NumPad8 = 96,
+ NumPad9 = 97,
+ NumPad0 = 98,
+ NumPadDot = 99,
+ Backslash = 100,
+ Application = 101,
+ Power = 102,
+ NumPadEquals = 103,
+ F13 = 104,
+ F14 = 105,
+ F15 = 106,
+ F16 = 107,
+ F17 = 108,
+ F18 = 109,
+ F19 = 110,
+ F20 = 111,
+ F21 = 112,
+ F22 = 113,
+ F23 = 114,
+ F24 = 115,
+ NumPadComma = 133,
+ Ro = 135,
+ KatakanaHiragana = 136,
+ Yen = 137,
+ Henkan = 138,
+ Muhenkan = 139,
+ NumPadCommaPc98 = 140,
+ HangulEnglish = 144,
+ Hanja = 145,
+ Katakana = 146,
+ Hiragana = 147,
+ ZenkakuHankaku = 148,
+ LeftControl = 224,
+ LeftShift = 225,
+ LeftAlt = 226,
+ LeftGui = 227,
+ RightControl = 228,
+ RightShift = 229,
+ RightAlt = 230,
+ RightGui = 231,
+};
+
+// This is nn::hid::NpadIdType
+enum class NpadIdType : u32 {
+ Player1 = 0x0,
+ Player2 = 0x1,
+ Player3 = 0x2,
+ Player4 = 0x3,
+ Player5 = 0x4,
+ Player6 = 0x5,
+ Player7 = 0x6,
+ Player8 = 0x7,
+ Other = 0x10,
+ Handheld = 0x20,
+
+ Invalid = 0xFFFFFFFF,
+};
+
+// This is nn::hid::NpadStyleIndex
+enum class NpadStyleIndex : u8 {
+ None = 0,
+ ProController = 3,
+ Handheld = 4,
+ HandheldNES = 4,
+ JoyconDual = 5,
+ JoyconLeft = 6,
+ JoyconRight = 7,
+ GameCube = 8,
+ Pokeball = 9,
+ NES = 10,
+ SNES = 12,
+ N64 = 13,
+ SegaGenesis = 14,
+ SystemExt = 32,
+ System = 33,
+ MaxNpadType = 34,
+};
+
+// This is nn::hid::NpadStyleSet
+enum class NpadStyleSet : u32 {
+ None = 0,
+ Fullkey = 1U << 0,
+ Handheld = 1U << 1,
+ JoyDual = 1U << 2,
+ JoyLeft = 1U << 3,
+ JoyRight = 1U << 4,
+ Gc = 1U << 5,
+ Palma = 1U << 6,
+ Lark = 1U << 7,
+ HandheldLark = 1U << 8,
+ Lucia = 1U << 9,
+ Lagoon = 1U << 10,
+ Lager = 1U << 11,
+ SystemExt = 1U << 29,
+ System = 1U << 30,
+
+ All = 0xFFFFFFFFU,
+};
+static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
+
+// This is nn::hid::VibrationDevicePosition
+enum class VibrationDevicePosition : u32 {
+ None = 0,
+ Left = 1,
+ Right = 2,
+};
+
+// This is nn::hid::VibrationDeviceType
+enum class VibrationDeviceType : u32 {
+ Unknown = 0,
+ LinearResonantActuator = 1,
+ GcErm = 2,
+};
+
+// This is nn::hid::VibrationGcErmCommand
+enum class VibrationGcErmCommand : u64 {
+ Stop = 0,
+ Start = 1,
+ StopHard = 2,
+};
+
+// This is nn::hid::NpadStyleTag
+struct NpadStyleTag {
+ union {
+ NpadStyleSet raw{};
+
+ BitField<0, 1, u32> fullkey;
+ BitField<1, 1, u32> handheld;
+ BitField<2, 1, u32> joycon_dual;
+ BitField<3, 1, u32> joycon_left;
+ BitField<4, 1, u32> joycon_right;
+ BitField<5, 1, u32> gamecube;
+ BitField<6, 1, u32> palma;
+ BitField<7, 1, u32> lark;
+ BitField<8, 1, u32> handheld_lark;
+ BitField<9, 1, u32> lucia;
+ BitField<10, 1, u32> lagoon;
+ BitField<11, 1, u32> lager;
+ BitField<29, 1, u32> system_ext;
+ BitField<30, 1, u32> system;
+ };
+};
+static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size");
+
+// This is nn::hid::TouchAttribute
+struct TouchAttribute {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32> start_touch;
+ BitField<1, 1, u32> end_touch;
+ };
+};
+static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
+
+// This is nn::hid::TouchState
+struct TouchState {
+ u64 delta_time;
+ TouchAttribute attribute;
+ u32 finger;
+ Common::Point<u32> position;
+ u32 diameter_x;
+ u32 diameter_y;
+ u32 rotation_angle;
+};
+static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
+
+// This is nn::hid::NpadControllerColor
+struct NpadControllerColor {
+ u32 body;
+ u32 button;
+};
+static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
+
+// This is nn::hid::AnalogStickState
+struct AnalogStickState {
+ s32 x;
+ s32 y;
+};
+static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");
+
+// This is nn::hid::server::NpadGcTriggerState
+struct NpadGcTriggerState {
+ s64 sampling_number{};
+ s32 left{};
+ s32 right{};
+};
+static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
+
+// This is nn::hid::system::NpadBatteryLevel
+using NpadBatteryLevel = u32;
+static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
+
+// This is nn::hid::system::NpadPowerInfo
+struct NpadPowerInfo {
+ bool is_powered;
+ bool is_charging;
+ INSERT_PADDING_BYTES(0x6);
+ NpadBatteryLevel battery_level;
+};
+static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
+
+struct LedPattern {
+ explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
+ position1.Assign(light1);
+ position2.Assign(light2);
+ position3.Assign(light3);
+ position4.Assign(light4);
+ }
+ union {
+ u64 raw{};
+ BitField<0, 1, u64> position1;
+ BitField<1, 1, u64> position2;
+ BitField<2, 1, u64> position3;
+ BitField<3, 1, u64> position4;
+ };
+};
+
+struct NpadButtonState {
+ union {
+ NpadButton raw{};
+
+ // Buttons
+ BitField<0, 1, u64> a;
+ BitField<1, 1, u64> b;
+ BitField<2, 1, u64> x;
+ BitField<3, 1, u64> y;
+ BitField<4, 1, u64> stick_l;
+ BitField<5, 1, u64> stick_r;
+ BitField<6, 1, u64> l;
+ BitField<7, 1, u64> r;
+ BitField<8, 1, u64> zl;
+ BitField<9, 1, u64> zr;
+ BitField<10, 1, u64> plus;
+ BitField<11, 1, u64> minus;
+
+ // D-Pad
+ BitField<12, 1, u64> left;
+ BitField<13, 1, u64> up;
+ BitField<14, 1, u64> right;
+ BitField<15, 1, u64> down;
+
+ // Left JoyStick
+ BitField<16, 1, u64> stick_l_left;
+ BitField<17, 1, u64> stick_l_up;
+ BitField<18, 1, u64> stick_l_right;
+ BitField<19, 1, u64> stick_l_down;
+
+ // Right JoyStick
+ BitField<20, 1, u64> stick_r_left;
+ BitField<21, 1, u64> stick_r_up;
+ BitField<22, 1, u64> stick_r_right;
+ BitField<23, 1, u64> stick_r_down;
+
+ BitField<24, 1, u64> left_sl;
+ BitField<25, 1, u64> left_sr;
+
+ BitField<26, 1, u64> right_sl;
+ BitField<27, 1, u64> right_sr;
+
+ BitField<28, 1, u64> palma;
+ BitField<29, 1, u64> verification;
+ BitField<30, 1, u64> handheld_left_b;
+ BitField<31, 1, u64> lagon_c_left;
+ BitField<32, 1, u64> lagon_c_up;
+ BitField<33, 1, u64> lagon_c_right;
+ BitField<34, 1, u64> lagon_c_down;
+ };
+};
+static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size.");
+
+// This is nn::hid::DebugPadButton
+struct DebugPadButton {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32> a;
+ BitField<1, 1, u32> b;
+ BitField<2, 1, u32> x;
+ BitField<3, 1, u32> y;
+ BitField<4, 1, u32> l;
+ BitField<5, 1, u32> r;
+ BitField<6, 1, u32> zl;
+ BitField<7, 1, u32> zr;
+ BitField<8, 1, u32> plus;
+ BitField<9, 1, u32> minus;
+ BitField<10, 1, u32> d_left;
+ BitField<11, 1, u32> d_up;
+ BitField<12, 1, u32> d_right;
+ BitField<13, 1, u32> d_down;
+ };
+};
+static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size");
+
+// This is nn::hid::ConsoleSixAxisSensorHandle
+struct ConsoleSixAxisSensorHandle {
+ u8 unknown_1;
+ u8 unknown_2;
+ INSERT_PADDING_BYTES_NOINIT(2);
+};
+static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
+ "ConsoleSixAxisSensorHandle is an invalid size");
+
+// This is nn::hid::SixAxisSensorHandle
+struct SixAxisSensorHandle {
+ NpadStyleIndex npad_type;
+ u8 npad_id;
+ DeviceIndex device_index;
+ INSERT_PADDING_BYTES_NOINIT(1);
+};
+static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
+
+struct SixAxisSensorFusionParameters {
+ f32 parameter1;
+ f32 parameter2;
+};
+static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
+ "SixAxisSensorFusionParameters is an invalid size");
+
+// This is nn::hid::VibrationDeviceHandle
+struct VibrationDeviceHandle {
+ NpadStyleIndex npad_type;
+ u8 npad_id;
+ DeviceIndex device_index;
+ INSERT_PADDING_BYTES_NOINIT(1);
+};
+static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");
+
+// This is nn::hid::VibrationValue
+struct VibrationValue {
+ f32 low_amplitude;
+ f32 low_frequency;
+ f32 high_amplitude;
+ f32 high_frequency;
+};
+static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
+
+// This is nn::hid::VibrationDeviceInfo
+struct VibrationDeviceInfo {
+ VibrationDeviceType type{};
+ VibrationDevicePosition position{};
+};
+static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
+
+// This is nn::hid::KeyboardModifier
+struct KeyboardModifier {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32> control;
+ BitField<1, 1, u32> shift;
+ BitField<2, 1, u32> left_alt;
+ BitField<3, 1, u32> right_alt;
+ BitField<4, 1, u32> gui;
+ BitField<8, 1, u32> caps_lock;
+ BitField<9, 1, u32> scroll_lock;
+ BitField<10, 1, u32> num_lock;
+ BitField<11, 1, u32> katakana;
+ BitField<12, 1, u32> hiragana;
+ };
+};
+
+static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size");
+
+// This is nn::hid::KeyboardAttribute
+struct KeyboardAttribute {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32> is_connected;
+ };
+};
+static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size");
+
+// This is nn::hid::KeyboardKey
+struct KeyboardKey {
+ // This should be a 256 bit flag
+ std::array<u8, 32> key;
+};
+static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
+
+// This is nn::hid::MouseButton
+struct MouseButton {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32> left;
+ BitField<1, 1, u32> right;
+ BitField<2, 1, u32> middle;
+ BitField<3, 1, u32> forward;
+ BitField<4, 1, u32> back;
+ };
+};
+static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size");
+
+// This is nn::hid::MouseAttribute
+struct MouseAttribute {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32> transferable;
+ BitField<1, 1, u32> is_connected;
+ };
+};
+static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size");
+
+// This is nn::hid::detail::MouseState
+struct MouseState {
+ s64 sampling_number;
+ s32 x;
+ s32 y;
+ s32 delta_x;
+ s32 delta_y;
+ // Axis Order in HW is switched for the wheel
+ s32 delta_wheel_y;
+ s32 delta_wheel_x;
+ MouseButton button;
+ MouseAttribute attribute;
+};
+static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
+
+/// Converts a NpadIdType to an array index.
+constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
+ switch (npad_id_type) {
+ case NpadIdType::Player1:
+ return 0;
+ case NpadIdType::Player2:
+ return 1;
+ case NpadIdType::Player3:
+ return 2;
+ case NpadIdType::Player4:
+ return 3;
+ case NpadIdType::Player5:
+ return 4;
+ case NpadIdType::Player6:
+ return 5;
+ case NpadIdType::Player7:
+ return 6;
+ case NpadIdType::Player8:
+ return 7;
+ case NpadIdType::Handheld:
+ return 8;
+ case NpadIdType::Other:
+ return 9;
+ default:
+ return 0;
+ }
+}
+
+/// Converts an array index to a NpadIdType
+constexpr NpadIdType IndexToNpadIdType(size_t index) {
+ switch (index) {
+ case 0:
+ return NpadIdType::Player1;
+ case 1:
+ return NpadIdType::Player2;
+ case 2:
+ return NpadIdType::Player3;
+ case 3:
+ return NpadIdType::Player4;
+ case 4:
+ return NpadIdType::Player5;
+ case 5:
+ return NpadIdType::Player6;
+ case 6:
+ return NpadIdType::Player7;
+ case 7:
+ return NpadIdType::Player8;
+ case 8:
+ return NpadIdType::Handheld;
+ case 9:
+ return NpadIdType::Other;
+ default:
+ return NpadIdType::Invalid;
+ }
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include <random>
+
+#include "common/input.h"
+#include "core/hid/input_converter.h"
+
+namespace Core::HID {
+
+Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) {
+ Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None};
+ switch (callback.type) {
+ case Common::Input::InputType::Analog:
+ case Common::Input::InputType::Trigger: {
+ const auto value = TransformToTrigger(callback).analog.value;
+ battery = Common::Input::BatteryLevel::Empty;
+ if (value > 0.2f) {
+ battery = Common::Input::BatteryLevel::Critical;
+ }
+ if (value > 0.4f) {
+ battery = Common::Input::BatteryLevel::Low;
+ }
+ if (value > 0.6f) {
+ battery = Common::Input::BatteryLevel::Medium;
+ }
+ if (value > 0.8f) {
+ battery = Common::Input::BatteryLevel::Full;
+ }
+ if (value >= 1.0f) {
+ battery = Common::Input::BatteryLevel::Charging;
+ }
+ break;
+ }
+ case Common::Input::InputType::Button:
+ battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging
+ : Common::Input::BatteryLevel::Critical;
+ break;
+ case Common::Input::InputType::Battery:
+ battery = callback.battery_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type);
+ break;
+ }
+
+ return battery;
+}
+
+Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) {
+ Common::Input::ButtonStatus status{};
+ switch (callback.type) {
+ case Common::Input::InputType::Analog:
+ case Common::Input::InputType::Trigger:
+ status.value = TransformToTrigger(callback).pressed.value;
+ break;
+ case Common::Input::InputType::Button:
+ status = callback.button_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
+ break;
+ }
+
+ if (status.inverted) {
+ status.value = !status.value;
+ }
+
+ return status;
+}
+
+Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) {
+ Common::Input::MotionStatus status{};
+ switch (callback.type) {
+ case Common::Input::InputType::Button: {
+ Common::Input::AnalogProperties properties{
+ .deadzone = 0.0f,
+ .range = 1.0f,
+ .offset = 0.0f,
+ };
+ status.delta_timestamp = 5000;
+ status.force_update = true;
+ status.accel.x = {
+ .value = 0.0f,
+ .raw_value = 0.0f,
+ .properties = properties,
+ };
+ status.accel.y = {
+ .value = 0.0f,
+ .raw_value = 0.0f,
+ .properties = properties,
+ };
+ status.accel.z = {
+ .value = 0.0f,
+ .raw_value = -1.0f,
+ .properties = properties,
+ };
+ status.gyro.x = {
+ .value = 0.0f,
+ .raw_value = 0.0f,
+ .properties = properties,
+ };
+ status.gyro.y = {
+ .value = 0.0f,
+ .raw_value = 0.0f,
+ .properties = properties,
+ };
+ status.gyro.z = {
+ .value = 0.0f,
+ .raw_value = 0.0f,
+ .properties = properties,
+ };
+ if (TransformToButton(callback).value) {
+ std::random_device device;
+ std::mt19937 gen(device());
+ std::uniform_int_distribution<s16> distribution(-1000, 1000);
+ status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
+ status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
+ status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
+ status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
+ status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
+ status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
+ }
+ break;
+ }
+ case Common::Input::InputType::Motion:
+ status = callback.motion_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type);
+ break;
+ }
+ SanitizeAnalog(status.accel.x, false);
+ SanitizeAnalog(status.accel.y, false);
+ SanitizeAnalog(status.accel.z, false);
+ SanitizeAnalog(status.gyro.x, false);
+ SanitizeAnalog(status.gyro.y, false);
+ SanitizeAnalog(status.gyro.z, false);
+
+ return status;
+}
+
+Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) {
+ Common::Input::StickStatus status{};
+
+ switch (callback.type) {
+ case Common::Input::InputType::Stick:
+ status = callback.stick_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type);
+ break;
+ }
+
+ SanitizeStick(status.x, status.y, true);
+ const auto& properties_x = status.x.properties;
+ const auto& properties_y = status.y.properties;
+ const float x = status.x.value;
+ const float y = status.y.value;
+
+ // Set directional buttons
+ status.right = x > properties_x.threshold;
+ status.left = x < -properties_x.threshold;
+ status.up = y > properties_y.threshold;
+ status.down = y < -properties_y.threshold;
+
+ return status;
+}
+
+Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) {
+ Common::Input::TouchStatus status{};
+
+ switch (callback.type) {
+ case Common::Input::InputType::Touch:
+ status = callback.touch_status;
+ break;
+ case Common::Input::InputType::Stick:
+ status.x = callback.stick_status.x;
+ status.y = callback.stick_status.y;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type);
+ break;
+ }
+
+ SanitizeAnalog(status.x, true);
+ SanitizeAnalog(status.y, true);
+ float& x = status.x.value;
+ float& y = status.y.value;
+
+ // Adjust if value is inverted
+ x = status.x.properties.inverted ? 1.0f + x : x;
+ y = status.y.properties.inverted ? 1.0f + y : y;
+
+ // clamp value
+ x = std::clamp(x, 0.0f, 1.0f);
+ y = std::clamp(y, 0.0f, 1.0f);
+
+ if (status.pressed.inverted) {
+ status.pressed.value = !status.pressed.value;
+ }
+
+ return status;
+}
+
+Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) {
+ Common::Input::TriggerStatus status{};
+ float& raw_value = status.analog.raw_value;
+ bool calculate_button_value = true;
+
+ switch (callback.type) {
+ case Common::Input::InputType::Analog:
+ status.analog.properties = callback.analog_status.properties;
+ raw_value = callback.analog_status.raw_value;
+ break;
+ case Common::Input::InputType::Button:
+ status.analog.properties.range = 1.0f;
+ status.analog.properties.inverted = callback.button_status.inverted;
+ raw_value = callback.button_status.value ? 1.0f : 0.0f;
+ break;
+ case Common::Input::InputType::Trigger:
+ status = callback.trigger_status;
+ calculate_button_value = false;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
+ break;
+ }
+
+ SanitizeAnalog(status.analog, true);
+ const auto& properties = status.analog.properties;
+ float& value = status.analog.value;
+
+ // Set button status
+ if (calculate_button_value) {
+ status.pressed.value = value > properties.threshold;
+ }
+
+ // Adjust if value is inverted
+ value = properties.inverted ? 1.0f + value : value;
+
+ // clamp value
+ value = std::clamp(value, 0.0f, 1.0f);
+
+ return status;
+}
+
+Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) {
+ Common::Input::AnalogStatus status{};
+
+ switch (callback.type) {
+ case Common::Input::InputType::Analog:
+ status.properties = callback.analog_status.properties;
+ status.raw_value = callback.analog_status.raw_value;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type);
+ break;
+ }
+
+ SanitizeAnalog(status, false);
+
+ // Adjust if value is inverted
+ status.value = status.properties.inverted ? -status.value : status.value;
+
+ return status;
+}
+
+void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
+ const auto& properties = analog.properties;
+ float& raw_value = analog.raw_value;
+ float& value = analog.value;
+
+ if (!std::isnormal(raw_value)) {
+ raw_value = 0;
+ }
+
+ // Apply center offset
+ raw_value -= properties.offset;
+
+ // Set initial values to be formated
+ value = raw_value;
+
+ // Calculate vector size
+ const float r = std::abs(value);
+
+ // Return zero if value is smaller than the deadzone
+ if (r <= properties.deadzone || properties.deadzone == 1.0f) {
+ analog.value = 0;
+ return;
+ }
+
+ // Adjust range of value
+ const float deadzone_factor =
+ 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone);
+ value = value * deadzone_factor / properties.range;
+
+ // Invert direction if needed
+ if (properties.inverted) {
+ value = -value;
+ }
+
+ // Clamp value
+ if (clamp_value) {
+ value = std::clamp(value, -1.0f, 1.0f);
+ }
+}
+
+void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
+ bool clamp_value) {
+ const auto& properties_x = analog_x.properties;
+ const auto& properties_y = analog_y.properties;
+ float& raw_x = analog_x.raw_value;
+ float& raw_y = analog_y.raw_value;
+ float& x = analog_x.value;
+ float& y = analog_y.value;
+
+ if (!std::isnormal(raw_x)) {
+ raw_x = 0;
+ }
+ if (!std::isnormal(raw_y)) {
+ raw_y = 0;
+ }
+
+ // Apply center offset
+ raw_x += properties_x.offset;
+ raw_y += properties_y.offset;
+
+ // Apply X scale correction from offset
+ if (std::abs(properties_x.offset) < 0.5f) {
+ if (raw_x > 0) {
+ raw_x /= 1 + properties_x.offset;
+ } else {
+ raw_x /= 1 - properties_x.offset;
+ }
+ }
+
+ // Apply Y scale correction from offset
+ if (std::abs(properties_y.offset) < 0.5f) {
+ if (raw_y > 0) {
+ raw_y /= 1 + properties_y.offset;
+ } else {
+ raw_y /= 1 - properties_y.offset;
+ }
+ }
+
+ // Invert direction if needed
+ raw_x = properties_x.inverted ? -raw_x : raw_x;
+ raw_y = properties_y.inverted ? -raw_y : raw_y;
+
+ // Set initial values to be formated
+ x = raw_x;
+ y = raw_y;
+
+ // Calculate vector size
+ float r = x * x + y * y;
+ r = std::sqrt(r);
+
+ // TODO(German77): Use deadzone and range of both axis
+
+ // Return zero if values are smaller than the deadzone
+ if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) {
+ x = 0;
+ y = 0;
+ return;
+ }
+
+ // Adjust range of joystick
+ const float deadzone_factor =
+ 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone);
+ x = x * deadzone_factor / properties_x.range;
+ y = y * deadzone_factor / properties_x.range;
+ r = r * deadzone_factor / properties_x.range;
+
+ // Normalize joystick
+ if (clamp_value && r > 1.0f) {
+ x /= r;
+ y /= r;
+ }
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#pragma once
+
+namespace Common::Input {
+struct CallbackStatus;
+enum class BatteryLevel : u32;
+using BatteryStatus = BatteryLevel;
+struct AnalogStatus;
+struct ButtonStatus;
+struct MotionStatus;
+struct StickStatus;
+struct TouchStatus;
+struct TriggerStatus;
+}; // namespace Common::Input
+
+namespace Core::HID {
+
+/**
+ * Converts raw input data into a valid battery status.
+ *
+ * @param callback Supported callbacks: Analog, Battery, Trigger.
+ * @return A valid BatteryStatus object.
+ */
+Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw input data into a valid button status. Applies invert properties to the output.
+ *
+ * @param callback Supported callbacks: Analog, Button, Trigger.
+ * @return A valid TouchStatus object.
+ */
+Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw input data into a valid motion status.
+ *
+ * @param callback Supported callbacks: Motion.
+ * @return A valid TouchStatus object.
+ */
+Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert
+ * properties to the output.
+ *
+ * @param callback Supported callbacks: Stick.
+ * @return A valid StickStatus object.
+ */
+Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw input data into a valid touch status.
+ *
+ * @param callback Supported callbacks: Touch.
+ * @return A valid TouchStatus object.
+ */
+Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and
+ * invert properties to the output. Button status uses the threshold property if necessary.
+ *
+ * @param callback Supported callbacks: Analog, Button, Trigger.
+ * @return A valid TriggerStatus object.
+ */
+Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw input data into a valid analog status. Applies offset, deadzone, range and
+ * invert properties to the output.
+ *
+ * @param callback Supported callbacks: Analog.
+ * @return A valid AnalogStatus object.
+ */
+Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw analog data into a valid analog value
+ * @param analog An analog object containing raw data and properties
+ * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
+ */
+void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value);
+
+/**
+ * Converts raw stick data into a valid stick value
+ * @param analog_x raw analog data and properties for the x-axis
+ * @param analog_y raw analog data and properties for the y-axis
+ * @param clamp_value bool that determines if the value needs to be clamped into the unit circle.
+ */
+void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
+ bool clamp_value);
+
+} // 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 @@
// Refer to the license.txt file included.
#include "core/core.h"
-#include "core/frontend/input_interpreter.h"
+#include "core/hid/hid_types.h"
+#include "core/hid/input_interpreter.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
@@ -19,7 +20,7 @@ InputInterpreter::InputInterpreter(Core::System& system)
InputInterpreter::~InputInterpreter() = default;
void InputInterpreter::PollInput() {
- const u32 button_state = npad.GetAndResetPressState();
+ const auto button_state = npad.GetAndResetPressState();
previous_index = current_index;
current_index = (current_index + 1) % button_states.size();
@@ -31,32 +32,30 @@ void InputInterpreter::ResetButtonStates() {
previous_index = 0;
current_index = 0;
- button_states[0] = 0xFFFFFFFF;
+ button_states[0] = Core::HID::NpadButton::All;
for (std::size_t i = 1; i < button_states.size(); ++i) {
- button_states[i] = 0;
+ button_states[i] = Core::HID::NpadButton::None;
}
}
-bool InputInterpreter::IsButtonPressed(HIDButton button) const {
- return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
+bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
+ return True(button_states[current_index] & button);
}
-bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const {
- const bool current_press =
- (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
- const bool previous_press =
- (button_states[previous_index] & (1U << static_cast<u8>(button))) != 0;
+bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
+ const bool current_press = True(button_states[current_index] & button);
+ const bool previous_press = True(button_states[previous_index] & button);
return current_press && !previous_press;
}
-bool InputInterpreter::IsButtonHeld(HIDButton button) const {
- u32 held_buttons{button_states[0]};
+bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
+ Core::HID::NpadButton held_buttons{button_states[0]};
for (std::size_t i = 1; i < button_states.size(); ++i) {
held_buttons &= button_states[i];
}
- return (held_buttons & (1U << static_cast<u8>(button))) != 0;
+ return True(held_buttons & button);
}
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 {
class System;
}
+namespace Core::HID {
+enum class NpadButton : u64;
+}
+
namespace Service::HID {
class Controller_NPad;
}
-enum class HIDButton : u8 {
- A,
- B,
- X,
- Y,
- LStick,
- RStick,
- L,
- R,
- ZL,
- ZR,
- Plus,
- Minus,
-
- DLeft,
- DUp,
- DRight,
- DDown,
-
- LStickLeft,
- LStickUp,
- LStickRight,
- LStickDown,
-
- RStickLeft,
- RStickUp,
- RStickRight,
- RStickDown,
-
- LeftSL,
- LeftSR,
-
- RightSL,
- RightSR,
-};
-
/**
* The InputInterpreter class interfaces with HID to retrieve button press states.
* Input is intended to be polled every 50ms so that a button is considered to be
@@ -76,7 +44,7 @@ public:
*
* @returns True when the button is pressed.
*/
- [[nodiscard]] bool IsButtonPressed(HIDButton button) const;
+ [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const;
/**
* Checks whether any of the buttons in the parameter list is pressed.
@@ -85,7 +53,7 @@ public:
*
* @returns True when at least one of the buttons is pressed.
*/
- template <HIDButton... T>
+ template <Core::HID::NpadButton... T>
[[nodiscard]] bool IsAnyButtonPressed() {
return (IsButtonPressed(T) || ...);
}
@@ -98,7 +66,7 @@ public:
*
* @returns True when the button is pressed once.
*/
- [[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const;
+ [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const;
/**
* Checks whether any of the buttons in the parameter list is pressed once.
@@ -107,7 +75,7 @@ public:
*
* @returns True when at least one of the buttons is pressed once.
*/
- template <HIDButton... T>
+ template <Core::HID::NpadButton... T>
[[nodiscard]] bool IsAnyButtonPressedOnce() const {
return (IsButtonPressedOnce(T) || ...);
}
@@ -119,7 +87,7 @@ public:
*
* @returns True when the button is held down.
*/
- [[nodiscard]] bool IsButtonHeld(HIDButton button) const;
+ [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const;
/**
* Checks whether any of the buttons in the parameter list is held down.
@@ -128,7 +96,7 @@ public:
*
* @returns True when at least one of the buttons is held down.
*/
- template <HIDButton... T>
+ template <Core::HID::NpadButton... T>
[[nodiscard]] bool IsAnyButtonHeld() const {
return (IsButtonHeld(T) || ...);
}
@@ -137,7 +105,7 @@ private:
Service::HID::Controller_NPad& npad;
/// Stores 9 consecutive button states polled from HID.
- std::array<u32, 9> button_states{};
+ std::array<Core::HID::NpadButton, 9> button_states{};
std::size_t previous_index{};
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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include "common/math_util.h"
+#include "core/hid/motion_input.h"
+
+namespace Core::HID {
+
+MotionInput::MotionInput() {
+ // Initialize PID constants with default values
+ SetPID(0.3f, 0.005f, 0.0f);
+}
+
+void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
+ kp = new_kp;
+ ki = new_ki;
+ kd = new_kd;
+}
+
+void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
+ accel = acceleration;
+}
+
+void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
+ gyro = gyroscope - gyro_drift;
+
+ // Auto adjust drift to minimize drift
+ if (!IsMoving(0.1f)) {
+ gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f);
+ }
+
+ if (gyro.Length2() < gyro_threshold) {
+ gyro = {};
+ } else {
+ only_accelerometer = false;
+ }
+}
+
+void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
+ quat = quaternion;
+}
+
+void MotionInput::SetGyroDrift(const Common::Vec3f& drift) {
+ gyro_drift = drift;
+}
+
+void MotionInput::SetGyroThreshold(f32 threshold) {
+ gyro_threshold = threshold;
+}
+
+void MotionInput::EnableReset(bool reset) {
+ reset_enabled = reset;
+}
+
+void MotionInput::ResetRotations() {
+ rotations = {};
+}
+
+bool MotionInput::IsMoving(f32 sensitivity) const {
+ return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f;
+}
+
+bool MotionInput::IsCalibrated(f32 sensitivity) const {
+ return real_error.Length() < sensitivity;
+}
+
+void MotionInput::UpdateRotation(u64 elapsed_time) {
+ const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
+ if (sample_period > 0.1f) {
+ return;
+ }
+ rotations += gyro * sample_period;
+}
+
+// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
+// 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
+void MotionInput::UpdateOrientation(u64 elapsed_time) {
+ if (!IsCalibrated(0.1f)) {
+ ResetOrientation();
+ }
+ // Short name local variable for readability
+ f32 q1 = quat.w;
+ f32 q2 = quat.xyz[0];
+ f32 q3 = quat.xyz[1];
+ f32 q4 = quat.xyz[2];
+ const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
+
+ // Ignore invalid elapsed time
+ if (sample_period > 0.1f) {
+ return;
+ }
+
+ const auto normal_accel = accel.Normalized();
+ auto rad_gyro = gyro * Common::PI * 2;
+ const f32 swap = rad_gyro.x;
+ rad_gyro.x = rad_gyro.y;
+ rad_gyro.y = -swap;
+ rad_gyro.z = -rad_gyro.z;
+
+ // Clear gyro values if there is no gyro present
+ if (only_accelerometer) {
+ rad_gyro.x = 0;
+ rad_gyro.y = 0;
+ rad_gyro.z = 0;
+ }
+
+ // Ignore drift correction if acceleration is not reliable
+ if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
+ const f32 ax = -normal_accel.x;
+ const f32 ay = normal_accel.y;
+ const f32 az = -normal_accel.z;
+
+ // Estimated direction of gravity
+ const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
+ const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
+ const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
+
+ // Error is cross product between estimated direction and measured direction of gravity
+ const Common::Vec3f new_real_error = {
+ az * vx - ax * vz,
+ ay * vz - az * vy,
+ ax * vy - ay * vx,
+ };
+
+ derivative_error = new_real_error - real_error;
+ real_error = new_real_error;
+
+ // Prevent integral windup
+ if (ki != 0.0f && !IsCalibrated(0.05f)) {
+ integral_error += real_error;
+ } else {
+ integral_error = {};
+ }
+
+ // Apply feedback terms
+ if (!only_accelerometer) {
+ rad_gyro += kp * real_error;
+ rad_gyro += ki * integral_error;
+ rad_gyro += kd * derivative_error;
+ } else {
+ // Give more weight to accelerometer values to compensate for the lack of gyro
+ rad_gyro += 35.0f * kp * real_error;
+ rad_gyro += 10.0f * ki * integral_error;
+ rad_gyro += 10.0f * kd * derivative_error;
+
+ // Emulate gyro values for games that need them
+ gyro.x = -rad_gyro.y;
+ gyro.y = rad_gyro.x;
+ gyro.z = -rad_gyro.z;
+ UpdateRotation(elapsed_time);
+ }
+ }
+
+ const f32 gx = rad_gyro.y;
+ const f32 gy = rad_gyro.x;
+ const f32 gz = rad_gyro.z;
+
+ // Integrate rate of change of quaternion
+ const f32 pa = q2;
+ const f32 pb = q3;
+ const f32 pc = q4;
+ q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
+ q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
+ q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
+ q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
+
+ quat.w = q1;
+ quat.xyz[0] = q2;
+ quat.xyz[1] = q3;
+ quat.xyz[2] = q4;
+ quat = quat.Normalized();
+}
+
+std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const {
+ const Common::Quaternion<float> quad{
+ .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w},
+ .w = -quat.xyz[2],
+ };
+ const std::array<float, 16> matrix4x4 = quad.ToMatrix();
+
+ return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
+ Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
+ Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
+}
+
+Common::Vec3f MotionInput::GetAcceleration() const {
+ return accel;
+}
+
+Common::Vec3f MotionInput::GetGyroscope() const {
+ return gyro;
+}
+
+Common::Quaternion<f32> MotionInput::GetQuaternion() const {
+ return quat;
+}
+
+Common::Vec3f MotionInput::GetRotations() const {
+ return rotations;
+}
+
+void MotionInput::ResetOrientation() {
+ if (!reset_enabled || only_accelerometer) {
+ return;
+ }
+ if (!IsMoving(0.5f) && accel.z <= -0.9f) {
+ ++reset_counter;
+ if (reset_counter > 900) {
+ quat.w = 0;
+ quat.xyz[0] = 0;
+ quat.xyz[1] = 0;
+ quat.xyz[2] = -1;
+ SetOrientationFromAccelerometer();
+ integral_error = {};
+ reset_counter = 0;
+ }
+ } else {
+ reset_counter = 0;
+ }
+}
+
+void MotionInput::SetOrientationFromAccelerometer() {
+ int iterations = 0;
+ const f32 sample_period = 0.015f;
+
+ const auto normal_accel = accel.Normalized();
+
+ while (!IsCalibrated(0.01f) && ++iterations < 100) {
+ // Short name local variable for readability
+ f32 q1 = quat.w;
+ f32 q2 = quat.xyz[0];
+ f32 q3 = quat.xyz[1];
+ f32 q4 = quat.xyz[2];
+
+ Common::Vec3f rad_gyro;
+ const f32 ax = -normal_accel.x;
+ const f32 ay = normal_accel.y;
+ const f32 az = -normal_accel.z;
+
+ // Estimated direction of gravity
+ const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
+ const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
+ const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
+
+ // Error is cross product between estimated direction and measured direction of gravity
+ const Common::Vec3f new_real_error = {
+ az * vx - ax * vz,
+ ay * vz - az * vy,
+ ax * vy - ay * vx,
+ };
+
+ derivative_error = new_real_error - real_error;
+ real_error = new_real_error;
+
+ rad_gyro += 10.0f * kp * real_error;
+ rad_gyro += 5.0f * ki * integral_error;
+ rad_gyro += 10.0f * kd * derivative_error;
+
+ const f32 gx = rad_gyro.y;
+ const f32 gy = rad_gyro.x;
+ const f32 gz = rad_gyro.z;
+
+ // Integrate rate of change of quaternion
+ const f32 pa = q2;
+ const f32 pb = q3;
+ const f32 pc = q4;
+ q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
+ q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
+ q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
+ q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
+
+ quat.w = q1;
+ quat.xyz[0] = q2;
+ quat.xyz[1] = q3;
+ quat.xyz[2] = q4;
+ quat = quat.Normalized();
+ }
+}
+} // 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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/quaternion.h"
+#include "common/vector_math.h"
+
+namespace Core::HID {
+
+class MotionInput {
+public:
+ explicit MotionInput();
+
+ MotionInput(const MotionInput&) = default;
+ MotionInput& operator=(const MotionInput&) = default;
+
+ MotionInput(MotionInput&&) = default;
+ MotionInput& operator=(MotionInput&&) = default;
+
+ void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
+ void SetAcceleration(const Common::Vec3f& acceleration);
+ void SetGyroscope(const Common::Vec3f& gyroscope);
+ void SetQuaternion(const Common::Quaternion<f32>& quaternion);
+ void SetGyroDrift(const Common::Vec3f& drift);
+ void SetGyroThreshold(f32 threshold);
+
+ void EnableReset(bool reset);
+ void ResetRotations();
+
+ void UpdateRotation(u64 elapsed_time);
+ void UpdateOrientation(u64 elapsed_time);
+
+ [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
+ [[nodiscard]] Common::Vec3f GetAcceleration() const;
+ [[nodiscard]] Common::Vec3f GetGyroscope() const;
+ [[nodiscard]] Common::Vec3f GetRotations() const;
+ [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
+
+ [[nodiscard]] bool IsMoving(f32 sensitivity) const;
+ [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
+
+private:
+ void ResetOrientation();
+ void SetOrientationFromAccelerometer();
+
+ // PID constants
+ f32 kp;
+ f32 ki;
+ f32 kd;
+
+ // PID errors
+ Common::Vec3f real_error;
+ Common::Vec3f integral_error;
+ Common::Vec3f derivative_error;
+
+ // Quaternion containing the device orientation
+ Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f};
+
+ // Number of full rotations in each axis
+ Common::Vec3f rotations;
+
+ // Acceleration vector measurement in G force
+ Common::Vec3f accel;
+
+ // Gyroscope vector measurement in radians/s.
+ Common::Vec3f gyro;
+
+ // Vector to be substracted from gyro measurements
+ Common::Vec3f gyro_drift;
+
+ // Minimum gyro amplitude to detect if the device is moving
+ f32 gyro_threshold = 0.0f;
+
+ // Number of invalid sequential data
+ u32 reset_counter = 0;
+
+ // If the provided data is invalid the device will be autocalibrated
+ bool reset_enabled = true;
+
+ // Use accelerometer values to calculate position
+ bool only_accelerometer = true;
+};
+
+} // 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 @@
#include "core/core.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
@@ -32,6 +33,7 @@ namespace Kernel::Init {
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
+ HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
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 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
@@ -28,7 +29,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
auto& monitor = system.Monitor();
- const auto current_core = system.CurrentCoreIndex();
+ const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -58,7 +59,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
auto& monitor = system.Monitor();
- const auto current_core = system.CurrentCoreIndex();
+ const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -85,6 +86,27 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
return true;
}
+class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
+public:
+ explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
+ : KThreadQueue(kernel_), m_tree(t) {}
+
+ void CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task) override {
+ // If the thread is waiting on an address arbiter, remove it from the tree.
+ if (waiting_thread->IsWaitingForAddressArbiter()) {
+ m_tree->erase(m_tree->iterator_to(*waiting_thread));
+ waiting_thread->ClearAddressArbiter();
+ }
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+
+private:
+ KAddressArbiter::ThreadTree* m_tree;
+};
+
} // namespace
ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
@@ -96,14 +118,14 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
+ // End the thread's wait.
KThread* target_thread = std::addressof(*it);
- target_thread->SetSyncedObject(nullptr, ResultSuccess);
+ target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
- target_thread->Wakeup();
+ target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
- target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -129,14 +151,14 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
+ // End the thread's wait.
KThread* target_thread = std::addressof(*it);
- target_thread->SetSyncedObject(nullptr, ResultSuccess);
+ target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
- target_thread->Wakeup();
+ target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
- target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -197,14 +219,14 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
+ // End the thread's wait.
KThread* target_thread = std::addressof(*it);
- target_thread->SetSyncedObject(nullptr, ResultSuccess);
+ target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
- target_thread->Wakeup();
+ target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
- target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -214,6 +236,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+ ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -224,9 +247,6 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
return ResultTerminationRequested;
}
- // Set the synced object.
- cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
-
// Read the value from userspace.
s32 user_value{};
bool succeeded{};
@@ -256,31 +276,20 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
- cur_thread->SetState(ThreadState::Waiting);
- cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
- }
-
- // Cancel the timer wait.
- kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
- // Remove from the address arbiter.
- {
- KScopedSchedulerLock sl(kernel);
-
- if (cur_thread->IsWaitingForAddressArbiter()) {
- thread_tree.erase(thread_tree.iterator_to(*cur_thread));
- cur_thread->ClearAddressArbiter();
- }
+ // Wait for the thread to finish.
+ cur_thread->BeginWait(std::addressof(wait_queue));
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Get the result.
- KSynchronizationObject* dummy{};
- return cur_thread->GetWaitResult(&dummy);
+ return cur_thread->GetWaitResult();
}
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+ ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -291,9 +300,6 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
return ResultTerminationRequested;
}
- // Set the synced object.
- cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
-
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, &user_value, addr)) {
@@ -316,26 +322,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
- cur_thread->SetState(ThreadState::Waiting);
- cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
- }
-
- // Cancel the timer wait.
- kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
- // Remove from the address arbiter.
- {
- KScopedSchedulerLock sl(kernel);
-
- if (cur_thread->IsWaitingForAddressArbiter()) {
- thread_tree.erase(thread_tree.iterator_to(*cur_thread));
- cur_thread->ClearAddressArbiter();
- }
+ // Wait for the thread to finish.
+ cur_thread->BeginWait(std::addressof(wait_queue));
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Get the result.
- KSynchronizationObject* dummy{};
- return cur_thread->GetWaitResult(&dummy);
+ return cur_thread->GetWaitResult();
}
} // 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:
}
}
+ const std::string& GetName() const {
+ return name;
+ }
+
private:
void RegisterWithKernel();
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 @@
#include "core/hle/kernel/k_class_token.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
@@ -48,7 +49,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
-// static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
+static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
// Ensure that the token hierarchy is correct.
@@ -79,7 +80,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut
static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
-// static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
// Ensure that the token hierarchy reflects the class hierarchy.
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_code_memory.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_memory_block.h"
+#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+KCodeMemory::KCodeMemory(KernelCore& kernel_)
+ : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
+
+ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
+ // Set members.
+ m_owner = kernel.CurrentProcess();
+
+ // Get the owner page table.
+ auto& page_table = m_owner->PageTable();
+
+ // Construct the page group.
+ KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
+ m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
+
+ // Lock the memory.
+ R_TRY(page_table.LockForCodeMemory(addr, size))
+
+ // Clear the memory.
+ for (const auto& block : m_page_group.Nodes()) {
+ std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
+ }
+
+ // Set remaining tracking members.
+ m_address = addr;
+ m_is_initialized = true;
+ m_is_owner_mapped = false;
+ m_is_mapped = false;
+
+ // We succeeded.
+ return ResultSuccess;
+}
+
+void KCodeMemory::Finalize() {
+ // Unlock.
+ if (!m_is_mapped && !m_is_owner_mapped) {
+ const size_t size = m_page_group.GetNumPages() * PageSize;
+ m_owner->PageTable().UnlockForCodeMemory(m_address, size);
+ }
+}
+
+ResultCode KCodeMemory::Map(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Ensure we're not already mapped.
+ R_UNLESS(!m_is_mapped, ResultInvalidState);
+
+ // Map the memory.
+ R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
+ address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
+
+ // Mark ourselves as mapped.
+ m_is_mapped = true;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Unmap the memory.
+ R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
+ KMemoryState::CodeOut));
+
+ // Mark ourselves as unmapped.
+ m_is_mapped = false;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Ensure we're not already mapped.
+ R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
+
+ // Convert the memory permission.
+ KMemoryPermission k_perm{};
+ switch (perm) {
+ case Svc::MemoryPermission::Read:
+ k_perm = KMemoryPermission::UserRead;
+ break;
+ case Svc::MemoryPermission::ReadExecute:
+ k_perm = KMemoryPermission::UserReadExecute;
+ break;
+ default:
+ break;
+ }
+
+ // Map the memory.
+ R_TRY(
+ m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
+
+ // Mark ourselves as mapped.
+ m_is_owner_mapped = true;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Unmap the memory.
+ R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
+
+ // Mark ourselves as unmapped.
+ m_is_owner_mapped = false;
+
+ return ResultSuccess;
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+enum class CodeMemoryOperation : u32 {
+ Map = 0,
+ MapToOwner = 1,
+ Unmap = 2,
+ UnmapFromOwner = 3,
+};
+
+class KCodeMemory final
+ : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
+ KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
+
+public:
+ explicit KCodeMemory(KernelCore& kernel_);
+
+ ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
+ void Finalize();
+
+ ResultCode Map(VAddr address, size_t size);
+ ResultCode Unmap(VAddr address, size_t size);
+ ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
+ ResultCode UnmapFromOwner(VAddr address, size_t size);
+
+ bool IsInitialized() const {
+ return m_is_initialized;
+ }
+ static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+ KProcess* GetOwner() const {
+ return m_owner;
+ }
+ VAddr GetSourceAddress() const {
+ return m_address;
+ }
+ size_t GetSize() const {
+ return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
+ }
+
+private:
+ KPageLinkedList m_page_group{};
+ KProcess* m_owner{};
+ VAddr m_address{};
+ KLightLock m_lock;
+ bool m_is_initialized{};
+ bool m_is_owner_mapped{};
+ bool m_is_mapped{};
+};
+
+} // 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 @@
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
@@ -33,7 +34,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
u32 new_orr_mask) {
auto& monitor = system.Monitor();
- const auto current_core = system.CurrentCoreIndex();
+ const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// Load the value from the address.
const auto expected = monitor.ExclusiveRead32(current_core, address);
@@ -57,6 +58,48 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero
return true;
}
+class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
+public:
+ explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
+ : KThreadQueue(kernel_) {}
+
+ void CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task) override {
+ // Remove the thread as a waiter from its owner.
+ waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+};
+
+class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
+private:
+ KConditionVariable::ThreadTree* m_tree;
+
+public:
+ explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
+ KernelCore& kernel_, KConditionVariable::ThreadTree* t)
+ : KThreadQueue(kernel_), m_tree(t) {}
+
+ void CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task) override {
+ // Remove the thread as a waiter from its owner.
+ if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
+ owner->RemoveWaiter(waiting_thread);
+ }
+
+ // If the thread is waiting on a condvar, remove it from the tree.
+ if (waiting_thread->IsWaitingForConditionVariable()) {
+ m_tree->erase(m_tree->iterator_to(*waiting_thread));
+ waiting_thread->ClearConditionVariable();
+ }
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+};
+
} // namespace
KConditionVariable::KConditionVariable(Core::System& system_)
@@ -78,84 +121,77 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
// Determine the next tag.
u32 next_value{};
- if (next_owner_thread) {
+ if (next_owner_thread != nullptr) {
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
- next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
- next_owner_thread->Wakeup();
- }
-
- // Write the value to userspace.
- if (!WriteToUser(system, addr, std::addressof(next_value))) {
- if (next_owner_thread) {
- next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
+ // Write the value to userspace.
+ ResultCode result{ResultSuccess};
+ if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
+ result = ResultSuccess;
+ } else {
+ result = ResultInvalidCurrentMemory;
}
- return ResultInvalidCurrentMemory;
+ // Signal the next owner thread.
+ next_owner_thread->EndWait(result);
+ return result;
+ } else {
+ // Just write the value to userspace.
+ R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
+ ResultInvalidCurrentMemory);
+
+ return ResultSuccess;
}
}
-
- return ResultSuccess;
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+ ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
+ KThread* owner_thread{};
{
- KScopedAutoObject<KThread> owner_thread;
- ASSERT(owner_thread.IsNull());
- {
- KScopedSchedulerLock sl(kernel);
- cur_thread->SetSyncedObject(nullptr, ResultSuccess);
+ KScopedSchedulerLock sl(kernel);
- // Check if the thread should terminate.
- R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
+ // Check if the thread should terminate.
+ R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
- {
- // Read the tag from userspace.
- u32 test_tag{};
- R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
- ResultInvalidCurrentMemory);
-
- // If the tag isn't the handle (with wait mask), we're done.
- R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
-
- // Get the lock owner thread.
- owner_thread =
- kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
- handle);
- R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
-
- // Update the lock.
- cur_thread->SetAddressKey(addr, value);
- owner_thread->AddWaiter(cur_thread);
- cur_thread->SetState(ThreadState::Waiting);
- cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
- cur_thread->SetMutexWaitAddressForDebugging(addr);
- }
- }
- ASSERT(owner_thread.IsNotNull());
- }
+ // Read the tag from userspace.
+ u32 test_tag{};
+ R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
- // Remove the thread as a waiter from the lock owner.
- {
- KScopedSchedulerLock sl(kernel);
- KThread* owner_thread = cur_thread->GetLockOwner();
- if (owner_thread != nullptr) {
- owner_thread->RemoveWaiter(cur_thread);
- }
+ // If the tag isn't the handle (with wait mask), we're done.
+ R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
+
+ // Get the lock owner thread.
+ owner_thread = kernel.CurrentProcess()
+ ->GetHandleTable()
+ .GetObjectWithoutPseudoHandle<KThread>(handle)
+ .ReleasePointerUnsafe();
+ R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
+
+ // Update the lock.
+ cur_thread->SetAddressKey(addr, value);
+ owner_thread->AddWaiter(cur_thread);
+
+ // Begin waiting.
+ cur_thread->BeginWait(std::addressof(wait_queue));
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
+ cur_thread->SetMutexWaitAddressForDebugging(addr);
}
+ // Close our reference to the owner thread, now that the wait is over.
+ owner_thread->Close();
+
// Get the wait result.
- KSynchronizationObject* dummy{};
- return cur_thread->GetWaitResult(std::addressof(dummy));
+ return cur_thread->GetWaitResult();
}
-KThread* KConditionVariable::SignalImpl(KThread* thread) {
+void KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -169,18 +205,16 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
- if (can_access) {
+ if (can_access) [[likely]] {
UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
- KThread* thread_to_close = nullptr;
- if (can_access) {
+ if (can_access) [[likely]] {
if (prev_tag == Svc::InvalidHandle) {
// If nobody held the lock previously, we're all good.
- thread->SetSyncedObject(nullptr, ResultSuccess);
- thread->Wakeup();
+ thread->EndWait(ResultSuccess);
} else {
// Get the previous owner.
KThread* owner_thread = kernel.CurrentProcess()
@@ -189,33 +223,22 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
- if (owner_thread) {
+ if (owner_thread) [[likely]] {
// Add the thread as a waiter on the owner.
owner_thread->AddWaiter(thread);
- thread_to_close = owner_thread;
+ owner_thread->Close();
} else {
// The lock was tagged with a thread that doesn't exist.
- thread->SetSyncedObject(nullptr, ResultInvalidState);
- thread->Wakeup();
+ thread->EndWait(ResultInvalidState);
}
}
} else {
// If the address wasn't accessible, note so.
- thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
- thread->Wakeup();
+ thread->EndWait(ResultInvalidCurrentMemory);
}
-
- return thread_to_close;
}
void KConditionVariable::Signal(u64 cv_key, s32 count) {
- // Prepare for signaling.
- constexpr int MaxThreads = 16;
-
- KLinkedList<KThread> thread_list{kernel};
- std::array<KThread*, MaxThreads> thread_array;
- s32 num_to_close{};
-
// Perform signaling.
s32 num_waiters{};
{
@@ -226,14 +249,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
- if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
- if (num_to_close < MaxThreads) {
- thread_array[num_to_close++] = thread;
- } else {
- thread_list.push_back(*thread);
- }
- }
-
+ this->SignalImpl(target_thread);
it = thread_tree.erase(it);
target_thread->ClearConditionVariable();
++num_waiters;
@@ -245,27 +261,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
}
}
-
- // Close threads in the array.
- for (auto i = 0; i < num_to_close; ++i) {
- thread_array[i]->Close();
- }
-
- // Close threads in the list.
- for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
- (*it).Close();
- }
}
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
- KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
+ ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
+ kernel, std::addressof(thread_tree));
{
- KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
-
- // Set the synced object.
- cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
+ KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@@ -290,8 +295,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
// Wake up the next owner.
- next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
- next_owner_thread->Wakeup();
+ next_owner_thread->EndWait(ResultSuccess);
}
// Write to the cv key.
@@ -308,40 +312,21 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
}
- // Update condition variable tracking.
- {
- cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
- thread_tree.insert(*cur_thread);
- }
+ // If timeout is zero, time out.
+ R_UNLESS(timeout != 0, ResultTimedOut);
- // If the timeout is non-zero, set the thread as waiting.
- if (timeout != 0) {
- cur_thread->SetState(ThreadState::Waiting);
- cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
- cur_thread->SetMutexWaitAddressForDebugging(addr);
- }
- }
-
- // Cancel the timer wait.
- kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
-
- // Remove from the condition variable.
- {
- KScopedSchedulerLock sl(kernel);
-
- if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
- owner->RemoveWaiter(cur_thread);
- }
+ // Update condition variable tracking.
+ cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
+ thread_tree.insert(*cur_thread);
- if (cur_thread->IsWaitingForConditionVariable()) {
- thread_tree.erase(thread_tree.iterator_to(*cur_thread));
- cur_thread->ClearConditionVariable();
- }
+ // Begin waiting.
+ cur_thread->BeginWait(std::addressof(wait_queue));
+ cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
+ cur_thread->SetMutexWaitAddressForDebugging(addr);
}
- // Get the result.
- KSynchronizationObject* dummy{};
- return cur_thread->GetWaitResult(std::addressof(dummy));
+ // Get the wait result.
+ return cur_thread->GetWaitResult();
}
} // 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:
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
- [[nodiscard]] KThread* SignalImpl(KThread* thread);
+ void SignalImpl(KThread* thread);
ThreadTree thread_tree;
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() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
+ KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
std::swap(m_table_size, saved_table_size);
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
// Find the object and free the entry.
KAutoObject* obj = nullptr;
{
+ KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if (this->IsValidHandle(handle)) {
@@ -62,6 +64,7 @@ bool KHandleTable::Remove(Handle handle) {
}
ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
+ KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -84,6 +87,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
}
ResultCode KHandleTable::Reserve(Handle* out_handle) {
+ KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -94,6 +98,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
}
void KHandleTable::Unreserve(Handle handle) {
+ KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
@@ -112,6 +117,7 @@ void KHandleTable::Unreserve(Handle handle) {
}
void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
+ KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// 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:
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
// Lock and look up in table.
+ KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -122,6 +123,7 @@ public:
size_t num_opened;
{
// Lock the table.
+ KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
// 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_light_condition_variable.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_thread_queue.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+namespace {
+
+class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
+public:
+ ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl,
+ bool term)
+ : KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
+
+ void CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task) override {
+ // Only process waits if we're allowed to.
+ if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
+ return;
+ }
+
+ // Remove the thread from the waiting thread from the light condition variable.
+ m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+
+private:
+ KThread::WaiterList* m_wait_list;
+ bool m_allow_terminating_thread;
+};
+
+} // namespace
+
+void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
+ // Create thread queue.
+ KThread* owner = GetCurrentThreadPointer(kernel);
+
+ ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
+ allow_terminating_thread);
+
+ // Sleep the thread.
+ {
+ KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
+
+ if (!allow_terminating_thread && owner->IsTerminationRequested()) {
+ lk.CancelSleep();
+ return;
+ }
+
+ lock->Unlock();
+
+ // Add the thread to the queue.
+ wait_list.push_back(*owner);
+
+ // Begin waiting.
+ owner->BeginWait(std::addressof(wait_queue));
+ }
+
+ // Re-acquire the lock.
+ lock->Lock();
+}
+
+void KLightConditionVariable::Broadcast() {
+ KScopedSchedulerLock lk(kernel);
+
+ // Signal all threads.
+ for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
+ it->EndWait(ResultSuccess);
+ }
+}
+
+} // 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-// This file references various implementation details from Atmosphere, an open-source firmware for
-// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
-
#pragma once
#include "common/common_types.h"
-#include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
-#include "core/hle/kernel/time_manager.h"
+#include "core/hle/kernel/k_thread.h"
namespace Kernel {
+
class KernelCore;
+class KLightLock;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
- void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) {
- WaitImpl(lock, timeout, allow_terminating_thread);
- }
-
- void Broadcast() {
- KScopedSchedulerLock lk{kernel};
-
- // Signal all threads.
- for (auto& thread : wait_list) {
- thread.SetState(ThreadState::Runnable);
- }
- }
+ void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
+ void Broadcast();
private:
- void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
- KThread* owner = GetCurrentThreadPointer(kernel);
-
- // Sleep the thread.
- {
- KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
-
- if (!allow_terminating_thread && owner->IsTerminationRequested()) {
- lk.CancelSleep();
- return;
- }
-
- lock->Unlock();
-
- // Set the thread as waiting.
- GetCurrentThread(kernel).SetState(ThreadState::Waiting);
-
- // Add the thread to the queue.
- wait_list.push_back(GetCurrentThread(kernel));
- }
-
- // Remove the thread from the wait list.
- {
- KScopedSchedulerLock sl{kernel};
-
- wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
- }
-
- // Cancel the task that the sleep setup.
- kernel.TimeManager().UnscheduleTimeEvent(owner);
-
- // Re-acquire the lock.
- lock->Lock();
- }
-
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
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 @@
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
+namespace {
+
+class ThreadQueueImplForKLightLock final : public KThreadQueue {
+public:
+ explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
+
+ void CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task) override {
+ // Remove the thread as a waiter from its owner.
+ if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
+ owner->RemoveWaiter(waiting_thread);
+ }
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+};
+
+} // namespace
+
void KLightLock::Lock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
- const uintptr_t cur_thread_tag = (cur_thread | 1);
while (true) {
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
- while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
+ while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
std::memory_order_acquire)) {
- if ((old_tag | 1) == cur_thread_tag) {
- return;
- }
}
- if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
+ if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
break;
}
-
- LockSlowPath(old_tag | 1, cur_thread);
}
}
void KLightLock::Unlock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
+
uintptr_t expected = cur_thread;
- do {
- if (expected != cur_thread) {
- return UnlockSlowPath(cur_thread);
- }
- } while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
+ if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
+ this->UnlockSlowPath(cur_thread);
+ }
}
-void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
+bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
+ ThreadQueueImplForKLightLock wait_queue(kernel);
// Pend the current thread waiting on the owner thread.
{
@@ -50,7 +65,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
// Ensure we actually have locking to do.
if (tag.load(std::memory_order_relaxed) != _owner) {
- return;
+ return false;
}
// Add the current thread as a waiter on the owner.
@@ -58,22 +73,15 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
- // Set thread states.
- cur_thread->SetState(ThreadState::Waiting);
+ // Begin waiting to hold the lock.
+ cur_thread->BeginWait(std::addressof(wait_queue));
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
}
}
- // We're no longer waiting on the lock owner.
- {
- KScopedSchedulerLock sl{kernel};
-
- if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
- owner_thread->RemoveWaiter(cur_thread);
- }
- }
+ return true;
}
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
@@ -81,22 +89,20 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
// Unlock.
{
- KScopedSchedulerLock sl{kernel};
+ KScopedSchedulerLock sl(kernel);
// Get the next owner.
- s32 num_waiters = 0;
+ s32 num_waiters;
KThread* next_owner = owner_thread->RemoveWaiterByKey(
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner != nullptr) {
- next_tag = reinterpret_cast<uintptr_t>(next_owner);
- if (num_waiters > 1) {
- next_tag |= 0x1;
- }
+ next_tag =
+ reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
- next_owner->SetState(ThreadState::Runnable);
+ next_owner->EndWait(ResultSuccess);
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
@@ -110,7 +116,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
}
// Write the new tag value.
- tag.store(next_tag);
+ tag.store(next_tag, std::memory_order_release);
}
}
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:
void Unlock();
- void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
+ bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
void UnlockSlowPath(uintptr_t cur_thread);
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 {
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
Svc::MemoryPermission::Execute),
+
+ KernelShift = 3,
+
+ KernelRead = Read << KernelShift,
+ KernelWrite = Write << KernelShift,
+ KernelExecute = Execute << KernelShift,
+
+ NotMapped = (1 << (2 * KernelShift)),
+
+ KernelReadWrite = KernelRead | KernelWrite,
+ KernelReadExecute = KernelRead | KernelExecute,
+
+ UserRead = Read | KernelRead,
+ UserWrite = Write | KernelWrite,
+ UserExecute = Execute,
+
+ UserReadWrite = UserRead | UserWrite,
+ UserReadExecute = UserRead | UserExecute,
+
+ IpcLockChangeMask = NotMapped | UserReadWrite
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
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:
return num_pages;
}
+ constexpr std::size_t GetSize() const {
+ return GetNumPages() * PageSize;
+ }
+
private:
u64 addr{};
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
return ResultSuccess;
}
+ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
+ KPageTable& src_page_table, VAddr src_addr) {
+ std::lock_guard lock{page_table_lock};
+
+ const std::size_t num_pages{size / PageSize};
+
+ // Check that the memory is mapped in the destination process.
+ size_t num_allocator_blocks;
+ R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
+ KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
+ KMemoryAttribute::None));
+
+ // Check that the memory is mapped in the source process.
+ R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
+ KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::None));
+
+ CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
+
+ // Apply the memory block update.
+ block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
+ KMemoryAttribute::None);
+
+ return ResultSuccess;
+}
void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
auto node{page_linked_list.Nodes().begin()};
PAddr map_addr{node->GetAddress()};
@@ -685,8 +712,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
return ResultSuccess;
}
-ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
- KMemoryPermission perm) {
+ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
+ KMemoryPermission perm) {
std::lock_guard lock{page_table_lock};
@@ -942,6 +969,60 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return ResultSuccess;
}
+ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
+
+ KMemoryPermission old_perm{};
+
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+ KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
+ result.IsError()) {
+ return result;
+ }
+
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
+ block->ShareToDevice(permission);
+ },
+ new_perm);
+
+ return ResultSuccess;
+}
+
+ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
+
+ KMemoryPermission old_perm{};
+
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+ KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::All, KMemoryAttribute::Locked)};
+ result.IsError()) {
+ return result;
+ }
+
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
+ block->UnshareToDevice(permission);
+ },
+ new_perm);
+
+ return ResultSuccess;
+}
+
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
block_manager = std::make_unique<KMemoryBlockManager>(start, end);
@@ -1231,4 +1312,42 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
return ResultSuccess;
}
+ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
+ // Get information about the first block.
+ const VAddr last_addr = addr + size - 1;
+ KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
+ KMemoryInfo info = it->GetMemoryInfo();
+
+ // If the start address isn't aligned, we need a block.
+ const size_t blocks_for_start_align =
+ (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
+
+ while (true) {
+ // Validate against the provided masks.
+ R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
+
+ // Break once we're done.
+ if (last_addr <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance our iterator.
+ it++;
+ info = it->GetMemoryInfo();
+ }
+
+ // If the end address isn't aligned, we need a block.
+ const size_t blocks_for_end_align =
+ (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
+
+ if (out_blocks_needed != nullptr) {
+ *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
+ }
+
+ return ResultSuccess;
+}
+
} // 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:
KMemoryPermission perm);
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
+ VAddr src_addr);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
ResultCode UnmapMemory(VAddr addr, std::size_t size);
@@ -41,7 +43,7 @@ public:
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
KMemoryPermission perm);
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
- ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
+ ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
KMemoryInfo QueryInfo(VAddr addr);
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
@@ -55,6 +57,8 @@ public:
KMemoryPermission perm, PAddr map_addr = 0);
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
+ ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
Common::PageTable& PageTableImpl() {
return page_table_impl;
@@ -115,6 +119,10 @@ private:
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr, ignore_attr);
}
+ ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
std::recursive_mutex page_table_lock;
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
thread->GetContext64().cpu_registers[0] = 0;
thread->GetContext32().cpu_registers[1] = thread_handle;
thread->GetContext64().cpu_registers[1] = thread_handle;
+ thread->DisableDispatch();
auto& kernel = system.Kernel();
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
@@ -227,12 +228,15 @@ void KProcess::PinCurrentThread() {
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
- // Pin it.
- PinThread(core_id, cur_thread);
- cur_thread->Pin();
+ // If the thread isn't terminated, pin it.
+ if (!cur_thread->IsTerminationRequested()) {
+ // Pin it.
+ PinThread(core_id, cur_thread);
+ cur_thread->Pin();
- // An update is needed.
- KScheduler::SetSchedulerUpdateNeeded(kernel);
+ // An update is needed.
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
+ }
}
void KProcess::UnpinCurrentThread() {
@@ -250,6 +254,20 @@ void KProcess::UnpinCurrentThread() {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
+void KProcess::UnpinThread(KThread* thread) {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+ // Get the thread's core id.
+ const auto core_id = thread->GetActiveCore();
+
+ // Unpin it.
+ UnpinThread(core_id, thread);
+ thread->Unpin();
+
+ // An update is needed.
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
+}
+
ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
[[maybe_unused]] size_t size) {
// Lock ourselves, to prevent concurrent access.
@@ -528,7 +546,7 @@ void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
std::lock_guard lock{HLE::g_hle_lock};
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
KMemoryPermission permission) {
- page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission);
+ page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
};
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:
void PinCurrentThread();
void UnpinCurrentThread();
+ void UnpinThread(KThread* thread);
KLightLock& GetStateLock() {
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
// If the thread is runnable, we want to change its priority in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
- GetPriorityQueue(kernel).ChangePriority(
- old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread);
+ GetPriorityQueue(kernel).ChangePriority(old_priority,
+ thread == kernel.GetCurrentEmuThread(), thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
}
@@ -360,7 +360,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
bool KScheduler::CanSchedule(KernelCore& kernel) {
- return kernel.CurrentScheduler()->GetCurrentThread()->GetDisableDispatchCount() <= 1;
+ return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
}
bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
@@ -376,20 +376,30 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
}
void KScheduler::DisableScheduling(KernelCore& kernel) {
- if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
- ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0);
- scheduler->GetCurrentThread()->DisableDispatch();
+ // If we are shutting down the kernel, none of this is relevant anymore.
+ if (kernel.IsShuttingDown()) {
+ return;
}
+
+ ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
+ GetCurrentThreadPointer(kernel)->DisableDispatch();
}
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
- if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
- ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
- if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
- scheduler->GetCurrentThread()->EnableDispatch();
- }
+ // If we are shutting down the kernel, none of this is relevant anymore.
+ if (kernel.IsShuttingDown()) {
+ return;
+ }
+
+ auto* current_thread = GetCurrentThreadPointer(kernel);
+
+ ASSERT(current_thread->GetDisableDispatchCount() >= 1);
+
+ if (current_thread->GetDisableDispatchCount() > 1) {
+ current_thread->EnableDispatch();
+ } else {
+ RescheduleCores(kernel, cores_needing_scheduling);
}
- RescheduleCores(kernel, cores_needing_scheduling);
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -617,13 +627,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
state.highest_priority_thread = nullptr;
}
-KScheduler::~KScheduler() {
+void KScheduler::Finalize() {
if (idle_thread) {
idle_thread->Close();
idle_thread = nullptr;
}
}
+KScheduler::~KScheduler() {
+ ASSERT(!idle_thread);
+}
+
KThread* KScheduler::GetCurrentThread() const {
if (auto result = current_thread.load(); result) {
return result;
@@ -642,10 +656,12 @@ void KScheduler::RescheduleCurrentCore() {
if (phys_core.IsInterrupted()) {
phys_core.ClearInterrupt();
}
+
guard.Lock();
if (state.needs_scheduling.load()) {
Schedule();
} else {
+ GetCurrentThread()->EnableDispatch();
guard.Unlock();
}
}
@@ -655,26 +671,33 @@ void KScheduler::OnThreadStart() {
}
void KScheduler::Unload(KThread* thread) {
+ ASSERT(thread);
+
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
- if (thread) {
- if (thread->IsCallingSvc()) {
- thread->ClearIsCallingSvc();
- }
- if (!thread->IsTerminationRequested()) {
- prev_thread = thread;
-
- Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
- cpu_core.SaveContext(thread->GetContext32());
- cpu_core.SaveContext(thread->GetContext64());
- // Save the TPIDR_EL0 system register in case it was modified.
- thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
- } else {
- prev_thread = nullptr;
- }
- thread->context_guard.Unlock();
+ if (thread->IsCallingSvc()) {
+ thread->ClearIsCallingSvc();
+ }
+
+ auto& physical_core = system.Kernel().PhysicalCore(core_id);
+ if (!physical_core.IsInitialized()) {
+ return;
+ }
+
+ Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
+ cpu_core.SaveContext(thread->GetContext32());
+ cpu_core.SaveContext(thread->GetContext64());
+ // Save the TPIDR_EL0 system register in case it was modified.
+ thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
+
+ if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
+ prev_thread = thread;
+ } else {
+ prev_thread = nullptr;
}
+
+ thread->context_guard.Unlock();
}
void KScheduler::Reload(KThread* thread) {
@@ -683,11 +706,6 @@ void KScheduler::Reload(KThread* thread) {
if (thread) {
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
- auto* const thread_owner_process = thread->GetOwnerProcess();
- if (thread_owner_process != nullptr) {
- system.Kernel().MakeCurrentProcess(thread_owner_process);
- }
-
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
@@ -705,7 +723,7 @@ void KScheduler::SwitchContextStep2() {
}
void KScheduler::ScheduleImpl() {
- KThread* previous_thread = current_thread.load();
+ KThread* previous_thread = GetCurrentThread();
KThread* next_thread = state.highest_priority_thread;
state.needs_scheduling = false;
@@ -717,10 +735,15 @@ void KScheduler::ScheduleImpl() {
// If we're not actually switching thread, there's nothing to do.
if (next_thread == current_thread.load()) {
+ previous_thread->EnableDispatch();
guard.Unlock();
return;
}
+ if (next_thread->GetCurrentCore() != core_id) {
+ next_thread->SetCurrentCore(core_id);
+ }
+
current_thread.store(next_thread);
KProcess* const previous_process = system.Kernel().CurrentProcess();
@@ -731,11 +754,7 @@ void KScheduler::ScheduleImpl() {
Unload(previous_thread);
std::shared_ptr<Common::Fiber>* old_context;
- if (previous_thread != nullptr) {
- old_context = &previous_thread->GetHostContext();
- } else {
- old_context = &idle_thread->GetHostContext();
- }
+ old_context = &previous_thread->GetHostContext();
guard.Unlock();
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:
explicit KScheduler(Core::System& system_, s32 core_id_);
~KScheduler();
+ void Finalize();
+
/// Reschedules to the next available thread (call after current thread is suspended)
void RescheduleCurrentCore();
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:
}
void Lock() {
+ // If we are shutting down the kernel, none of this is relevant anymore.
+ if (kernel.IsShuttingDown()) {
+ return;
+ }
+
if (IsLockedByCurrentThread()) {
// If we already own the lock, we can just increment the count.
ASSERT(lock_count > 0);
@@ -43,6 +48,11 @@ public:
}
void Unlock() {
+ // If we are shutting down the kernel, none of this is relevant anymore.
+ if (kernel.IsShuttingDown()) {
+ return;
+ }
+
ASSERT(IsLockedByCurrentThread());
ASSERT(lock_count > 0);
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 @@
#pragma once
#include "common/common_types.h"
+#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#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) {
{
KScopedSchedulerLock lock(kernel);
if (!context.IsThreadWaiting()) {
- context.GetThread().Wakeup();
- context.GetThread().SetSyncedObject(nullptr, result);
+ context.GetThread().EndWait(result);
}
}
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 @@
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
+namespace {
+
+class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
+public:
+ ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
+ KSynchronizationObject::ThreadListNode* n, s32 c)
+ : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
+
+ void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
+ ResultCode wait_result) override {
+ // Determine the sync index, and unlink all nodes.
+ s32 sync_index = -1;
+ for (auto i = 0; i < m_count; ++i) {
+ // Check if this is the signaled object.
+ if (m_objects[i] == signaled_object && sync_index == -1) {
+ sync_index = i;
+ }
+
+ // Unlink the current node from the current object.
+ m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
+ }
+
+ // Set the waiting thread's sync index.
+ waiting_thread->SetSyncedIndex(sync_index);
+
+ // Set the waiting thread as not cancellable.
+ waiting_thread->ClearCancellable();
+
+ // Invoke the base end wait handler.
+ KThreadQueue::EndWait(waiting_thread, wait_result);
+ }
+
+ void CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task) override {
+ // Remove all nodes from our list.
+ for (auto i = 0; i < m_count; ++i) {
+ m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
+ }
+
+ // Set the waiting thread as not cancellable.
+ waiting_thread->ClearCancellable();
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+
+private:
+ KSynchronizationObject** m_objects;
+ KSynchronizationObject::ThreadListNode* m_nodes;
+ s32 m_count;
+};
+
+} // namespace
+
void KSynchronizationObject::Finalize() {
this->OnFinalizeSynchronizationObject();
KAutoObject::Finalize();
@@ -25,11 +80,19 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
std::vector<ThreadListNode> thread_nodes(num_objects);
// Prepare for wait.
- KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread();
+ KThread* thread = GetCurrentThreadPointer(kernel_ctx);
+ ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
+ thread_nodes.data(), num_objects);
{
// Setup the scheduling lock and sleep.
- KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout};
+ KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
+
+ // Check if the thread should terminate.
+ if (thread->IsTerminationRequested()) {
+ slp.CancelSleep();
+ return ResultTerminationRequested;
+ }
// Check if any of the objects are already signaled.
for (auto i = 0; i < num_objects; ++i) {
@@ -48,12 +111,6 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
return ResultTimedOut;
}
- // Check if the thread should terminate.
- if (thread->IsTerminationRequested()) {
- slp.CancelSleep();
- return ResultTerminationRequested;
- }
-
// Check if waiting was canceled.
if (thread->IsWaitCancelled()) {
slp.CancelSleep();
@@ -66,73 +123,25 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
thread_nodes[i].thread = thread;
thread_nodes[i].next = nullptr;
- if (objects[i]->thread_list_tail == nullptr) {
- objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
- } else {
- objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
- }
-
- objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
+ objects[i]->LinkNode(std::addressof(thread_nodes[i]));
}
- // For debugging only
- thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
-
- // Mark the thread as waiting.
+ // Mark the thread as cancellable.
thread->SetCancellable();
- thread->SetSyncedObject(nullptr, ResultTimedOut);
- thread->SetState(ThreadState::Waiting);
- thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
- }
- // The lock/sleep is done, so we should be able to get our result.
+ // Clear the thread's synced index.
+ thread->SetSyncedIndex(-1);
- // Thread is no longer cancellable.
- thread->ClearCancellable();
-
- // For debugging only
- thread->SetWaitObjectsForDebugging({});
+ // Wait for an object to be signaled.
+ thread->BeginWait(std::addressof(wait_queue));
+ thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
+ }
- // Cancel the timer as needed.
- kernel_ctx.TimeManager().UnscheduleTimeEvent(thread);
+ // Set the output index.
+ *out_index = thread->GetSyncedIndex();
// Get the wait result.
- ResultCode wait_result{ResultSuccess};
- s32 sync_index = -1;
- {
- KScopedSchedulerLock lock(kernel_ctx);
- KSynchronizationObject* synced_obj;
- wait_result = thread->GetWaitResult(std::addressof(synced_obj));
-
- for (auto i = 0; i < num_objects; ++i) {
- // Unlink the object from the list.
- ThreadListNode* prev_ptr =
- reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
- ThreadListNode* prev_val = nullptr;
- ThreadListNode *prev, *tail_prev;
-
- do {
- prev = prev_ptr;
- prev_ptr = prev_ptr->next;
- tail_prev = prev_val;
- prev_val = prev_ptr;
- } while (prev_ptr != std::addressof(thread_nodes[i]));
-
- if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
- objects[i]->thread_list_tail = tail_prev;
- }
-
- prev->next = thread_nodes[i].next;
-
- if (objects[i] == synced_obj) {
- sync_index = i;
- }
- }
- }
-
- // Set output.
- *out_index = sync_index;
- return wait_result;
+ return thread->GetWaitResult();
}
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
@@ -141,7 +150,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
KSynchronizationObject::~KSynchronizationObject() = default;
void KSynchronizationObject::NotifyAvailable(ResultCode result) {
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl(kernel);
// If we're not signaled, we've nothing to notify.
if (!this->IsSignaled()) {
@@ -150,11 +159,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
// Iterate over each thread.
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
- KThread* thread = cur_node->thread;
- if (thread->GetState() == ThreadState::Waiting) {
- thread->SetSyncedObject(this, result);
- thread->SetState(ThreadState::Runnable);
- }
+ cur_node->thread->NotifyAvailable(this, result);
}
}
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:
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
+ void LinkNode(ThreadListNode* node_) {
+ // Link the node to the list.
+ if (thread_list_tail == nullptr) {
+ thread_list_head = node_;
+ } else {
+ thread_list_tail->next = node_;
+ }
+
+ thread_list_tail = node_;
+ }
+
+ void UnlinkNode(ThreadListNode* node_) {
+ // Unlink the node from the list.
+ ThreadListNode* prev_ptr =
+ reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
+ ThreadListNode* prev_val = nullptr;
+ ThreadListNode *prev, *tail_prev;
+
+ do {
+ prev = prev_ptr;
+ prev_ptr = prev_ptr->next;
+ tail_prev = prev_val;
+ prev_val = prev_ptr;
+ } while (prev_ptr != node_);
+
+ if (thread_list_tail == node_) {
+ thread_list_tail = tail_prev;
+ }
+
+ prev->next = node_->next;
+ }
+
protected:
explicit KSynchronizationObject(KernelCore& kernel);
~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 @@
#include "common/common_types.h"
#include "common/fiber.h"
#include "common/logging/log.h"
+#include "common/scope_exit.h"
+#include "common/settings.h"
+#include "common/thread_queue_list.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
@@ -56,6 +59,34 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
namespace Kernel {
+namespace {
+
+class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
+public:
+ explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
+ : KThreadQueueWithoutEndWait(kernel_) {}
+};
+
+class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue {
+public:
+ explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl)
+ : KThreadQueue(kernel_), m_wait_list(wl) {}
+
+ void CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task) override {
+ // Remove the thread from the wait list.
+ m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+
+private:
+ KThread::WaiterList* m_wait_list;
+};
+
+} // namespace
+
KThread::KThread(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {}
KThread::~KThread() = default;
@@ -82,6 +113,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
[[fallthrough]];
case ThreadType::HighPriority:
[[fallthrough]];
+ case ThreadType::Dummy:
+ [[fallthrough]];
case ThreadType::User:
ASSERT(((owner == nullptr) ||
(owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
@@ -127,11 +160,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
priority = prio;
base_priority = prio;
- // Set sync object and waiting lock to null.
- synced_object = nullptr;
-
// Initialize sleeping queue.
- sleeping_queue = nullptr;
+ wait_queue = nullptr;
// Set suspend flags.
suspend_request_flags = 0;
@@ -184,7 +214,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
// Setup the stack parameters.
StackParameters& sp = GetStackParameters();
sp.cur_thread = this;
- sp.disable_count = 1;
+ sp.disable_count = 0;
SetInExceptionHandler();
// Set thread ID.
@@ -211,15 +241,16 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
// Initialize the thread.
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
- // Initialize host context.
+ // Initialize emulation parameters.
thread->host_context =
std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
+ thread->is_single_core = !Settings::values.use_multi_core.GetValue();
return ResultSuccess;
}
ResultCode KThread::InitializeDummyThread(KThread* thread) {
- return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Main);
+ return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy);
}
ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
@@ -273,11 +304,14 @@ void KThread::Finalize() {
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
- // The thread shouldn't be a kernel waiter.
+ // Clear the lock owner
it->SetLockOwner(nullptr);
- it->SetSyncedObject(nullptr, ResultInvalidState);
- it->Wakeup();
+
+ // Erase the waiter from our list.
it = waiter_list.erase(it);
+
+ // Cancel the thread's wait.
+ it->CancelWait(ResultInvalidState, true);
}
}
@@ -294,15 +328,12 @@ bool KThread::IsSignaled() const {
return signaled;
}
-void KThread::Wakeup() {
- KScopedSchedulerLock sl{kernel};
+void KThread::OnTimer() {
+ ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ // If we're waiting, cancel the wait.
if (GetState() == ThreadState::Waiting) {
- if (sleeping_queue != nullptr) {
- sleeping_queue->WakeupThread(this);
- } else {
- SetState(ThreadState::Runnable);
- }
+ wait_queue->CancelWait(this, ResultTimedOut, false);
}
}
@@ -327,7 +358,7 @@ void KThread::StartTermination() {
// Signal.
signaled = true;
- NotifyAvailable();
+ KSynchronizationObject::NotifyAvailable();
// Clear previous thread in KScheduler.
KScheduler::ClearPreviousThread(kernel, this);
@@ -475,30 +506,32 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
return ResultSuccess;
}
-ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
+ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
ASSERT(parent != nullptr);
ASSERT(v_affinity_mask != 0);
- KScopedLightLock lk{activity_pause_lock};
+ KScopedLightLock lk(activity_pause_lock);
// Set the core mask.
u64 p_affinity_mask = 0;
{
- KScopedSchedulerLock sl{kernel};
+ KScopedSchedulerLock sl(kernel);
ASSERT(num_core_migration_disables >= 0);
- // If the core id is no-update magic, preserve the ideal core id.
- if (cpu_core_id == Svc::IdealCoreNoUpdate) {
- cpu_core_id = virtual_ideal_core_id;
- R_UNLESS(((1ULL << cpu_core_id) & v_affinity_mask) != 0, ResultInvalidCombination);
+ // If we're updating, set our ideal virtual core.
+ if (core_id_ != Svc::IdealCoreNoUpdate) {
+ virtual_ideal_core_id = core_id_;
+ } else {
+ // Preserve our ideal core id.
+ core_id_ = virtual_ideal_core_id;
+ R_UNLESS(((1ULL << core_id_) & v_affinity_mask) != 0, ResultInvalidCombination);
}
- // Set the virtual core/affinity mask.
- virtual_ideal_core_id = cpu_core_id;
+ // Set our affinity mask.
virtual_affinity_mask = v_affinity_mask;
// Translate the virtual core to a physical core.
- if (cpu_core_id >= 0) {
- cpu_core_id = Core::Hardware::VirtualToPhysicalCoreMap[cpu_core_id];
+ if (core_id_ >= 0) {
+ core_id_ = Core::Hardware::VirtualToPhysicalCoreMap[core_id_];
}
// Translate the virtual affinity mask to a physical one.
@@ -513,7 +546,7 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
const KAffinityMask old_mask = physical_affinity_mask;
// Set our new ideals.
- physical_ideal_core_id = cpu_core_id;
+ physical_ideal_core_id = core_id_;
physical_affinity_mask.SetAffinityMask(p_affinity_mask);
if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) {
@@ -531,18 +564,18 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
}
} else {
// Otherwise, we edit the original affinity for restoration later.
- original_physical_ideal_core_id = cpu_core_id;
+ original_physical_ideal_core_id = core_id_;
original_physical_affinity_mask.SetAffinityMask(p_affinity_mask);
}
}
// Update the pinned waiter list.
+ ThreadQueueImplForKThreadSetProperty wait_queue_(kernel, std::addressof(pinned_waiter_list));
{
bool retry_update{};
- bool thread_is_pinned{};
do {
// Lock the scheduler.
- KScopedSchedulerLock sl{kernel};
+ KScopedSchedulerLock sl(kernel);
// Don't do any further management if our termination has been requested.
R_SUCCEED_IF(IsTerminationRequested());
@@ -570,12 +603,9 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
ResultTerminationRequested);
- // Note that the thread was pinned.
- thread_is_pinned = true;
-
// Wait until the thread isn't pinned any more.
pinned_waiter_list.push_back(GetCurrentThread(kernel));
- GetCurrentThread(kernel).SetState(ThreadState::Waiting);
+ GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
} else {
// If the thread isn't pinned, release the scheduler lock and retry until it's
// not current.
@@ -583,16 +613,6 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
}
}
} while (retry_update);
-
- // If the thread was pinned, it no longer is, and we should remove the current thread from
- // our waiter list.
- if (thread_is_pinned) {
- // Lock the scheduler.
- KScopedSchedulerLock sl{kernel};
-
- // Remove from the list.
- pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
- }
}
return ResultSuccess;
@@ -641,15 +661,9 @@ void KThread::WaitCancel() {
KScopedSchedulerLock sl{kernel};
// Check if we're waiting and cancellable.
- if (GetState() == ThreadState::Waiting && cancellable) {
- if (sleeping_queue != nullptr) {
- sleeping_queue->WakeupThread(this);
- wait_cancelled = true;
- } else {
- SetSyncedObject(nullptr, ResultCancelled);
- SetState(ThreadState::Runnable);
- wait_cancelled = false;
- }
+ if (this->GetState() == ThreadState::Waiting && cancellable) {
+ wait_cancelled = false;
+ wait_queue->CancelWait(this, ResultCancelled, true);
} else {
// Otherwise, note that we cancelled a wait.
wait_cancelled = true;
@@ -700,60 +714,59 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
// Set the activity.
{
// Lock the scheduler.
- KScopedSchedulerLock sl{kernel};
+ KScopedSchedulerLock sl(kernel);
// Verify our state.
- const auto cur_state = GetState();
+ const auto cur_state = this->GetState();
R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable),
ResultInvalidState);
// Either pause or resume.
if (activity == Svc::ThreadActivity::Paused) {
// Verify that we're not suspended.
- R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
+ R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// Suspend.
- RequestSuspend(SuspendType::Thread);
+ this->RequestSuspend(SuspendType::Thread);
} else {
ASSERT(activity == Svc::ThreadActivity::Runnable);
// Verify that we're suspended.
- R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
+ R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// Resume.
- Resume(SuspendType::Thread);
+ this->Resume(SuspendType::Thread);
}
}
// If the thread is now paused, update the pinned waiter list.
if (activity == Svc::ThreadActivity::Paused) {
- bool thread_is_pinned{};
- bool thread_is_current{};
+ ThreadQueueImplForKThreadSetProperty wait_queue_(kernel,
+ std::addressof(pinned_waiter_list));
+
+ bool thread_is_current;
do {
// Lock the scheduler.
- KScopedSchedulerLock sl{kernel};
+ KScopedSchedulerLock sl(kernel);
// Don't do any further management if our termination has been requested.
- R_SUCCEED_IF(IsTerminationRequested());
+ R_SUCCEED_IF(this->IsTerminationRequested());
+
+ // By default, treat the thread as not current.
+ thread_is_current = false;
// Check whether the thread is pinned.
- if (GetStackParameters().is_pinned) {
+ if (this->GetStackParameters().is_pinned) {
// Verify that the current thread isn't terminating.
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
ResultTerminationRequested);
- // Note that the thread was pinned and not current.
- thread_is_pinned = true;
- thread_is_current = false;
-
// Wait until the thread isn't pinned any more.
pinned_waiter_list.push_back(GetCurrentThread(kernel));
- GetCurrentThread(kernel).SetState(ThreadState::Waiting);
+ GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
} else {
// Check if the thread is currently running.
// If it is, we'll need to retry.
- thread_is_current = false;
-
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
if (kernel.Scheduler(i).GetCurrentThread() == this) {
thread_is_current = true;
@@ -762,16 +775,6 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
}
}
} while (thread_is_current);
-
- // If the thread was pinned, it no longer is, and we should remove the current thread from
- // our waiter list.
- if (thread_is_pinned) {
- // Lock the scheduler.
- KScopedSchedulerLock sl{kernel};
-
- // Remove from the list.
- pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
- }
}
return ResultSuccess;
@@ -966,6 +969,9 @@ ResultCode KThread::Run() {
// Set our state and finish.
SetState(ThreadState::Runnable);
+
+ DisableDispatch();
+
return ResultSuccess;
}
}
@@ -996,27 +1002,61 @@ ResultCode KThread::Sleep(s64 timeout) {
ASSERT(this == GetCurrentThreadPointer(kernel));
ASSERT(timeout > 0);
+ ThreadQueueImplForKThreadSleep wait_queue_(kernel);
{
// Setup the scheduling lock and sleep.
- KScopedSchedulerLockAndSleep slp{kernel, this, timeout};
+ KScopedSchedulerLockAndSleep slp(kernel, this, timeout);
// Check if the thread should terminate.
- if (IsTerminationRequested()) {
+ if (this->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
- // Mark the thread as waiting.
- SetState(ThreadState::Waiting);
+ // Wait for the sleep to end.
+ this->BeginWait(std::addressof(wait_queue_));
SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
}
- // The lock/sleep is done.
+ return ResultSuccess;
+}
- // Cancel the timer.
- kernel.TimeManager().UnscheduleTimeEvent(this);
+void KThread::BeginWait(KThreadQueue* queue) {
+ // Set our state as waiting.
+ SetState(ThreadState::Waiting);
- return ResultSuccess;
+ // Set our wait queue.
+ wait_queue = queue;
+}
+
+void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl(kernel);
+
+ // If we're waiting, notify our queue that we're available.
+ if (GetState() == ThreadState::Waiting) {
+ wait_queue->NotifyAvailable(this, signaled_object, wait_result_);
+ }
+}
+
+void KThread::EndWait(ResultCode wait_result_) {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl(kernel);
+
+ // If we're waiting, notify our queue that we're available.
+ if (GetState() == ThreadState::Waiting) {
+ wait_queue->EndWait(this, wait_result_);
+ }
+}
+
+void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl(kernel);
+
+ // If we're waiting, notify our queue that we're available.
+ if (GetState() == ThreadState::Waiting) {
+ wait_queue->CancelWait(this, wait_result_, cancel_timer_task);
+ }
}
void KThread::SetState(ThreadState state) {
@@ -1050,4 +1090,26 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
return GetCurrentThread(kernel).GetCurrentCore();
}
+KScopedDisableDispatch::~KScopedDisableDispatch() {
+ // If we are shutting down the kernel, none of this is relevant anymore.
+ if (kernel.IsShuttingDown()) {
+ return;
+ }
+
+ // Skip the reschedule if single-core, as dispatch tracking is disabled here.
+ if (!Settings::values.use_multi_core.GetValue()) {
+ return;
+ }
+
+ if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
+ auto scheduler = kernel.CurrentScheduler();
+
+ if (scheduler) {
+ scheduler->RescheduleCurrentCore();
+ }
+ } else {
+ GetCurrentThread(kernel).EnableDispatch();
+ }
+}
+
} // 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 {
Kernel = 1,
HighPriority = 2,
User = 3,
+ Dummy = 100, // Special thread type for emulation purposes only
};
DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
@@ -161,8 +162,6 @@ public:
}
}
- void Wakeup();
-
void SetBasePriority(s32 value);
[[nodiscard]] ResultCode Run();
@@ -197,13 +196,19 @@ public:
void Suspend();
- void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
- synced_object = obj;
+ constexpr void SetSyncedIndex(s32 index) {
+ synced_index = index;
+ }
+
+ [[nodiscard]] constexpr s32 GetSyncedIndex() const {
+ return synced_index;
+ }
+
+ constexpr void SetWaitResult(ResultCode wait_res) {
wait_result = wait_res;
}
- [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
- *out = synced_object;
+ [[nodiscard]] constexpr ResultCode GetWaitResult() const {
return wait_result;
}
@@ -374,6 +379,8 @@ public:
[[nodiscard]] bool IsSignaled() const override;
+ void OnTimer();
+
static void PostDestroy(uintptr_t arg);
[[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
@@ -446,20 +453,39 @@ public:
return per_core_priority_queue_entry[core];
}
- void SetSleepingQueue(KThreadQueue* q) {
- sleeping_queue = q;
+ [[nodiscard]] bool IsKernelThread() const {
+ return GetActiveCore() == 3;
+ }
+
+ [[nodiscard]] bool IsDispatchTrackingDisabled() const {
+ return is_single_core || IsKernelThread();
}
[[nodiscard]] s32 GetDisableDispatchCount() const {
+ if (IsDispatchTrackingDisabled()) {
+ // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
+ return 1;
+ }
+
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
+ if (IsDispatchTrackingDisabled()) {
+ // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
+ return;
+ }
+
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
+ if (IsDispatchTrackingDisabled()) {
+ // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
+ return;
+ }
+
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
@@ -573,6 +599,15 @@ public:
address_key_value = val;
}
+ void ClearWaitQueue() {
+ wait_queue = nullptr;
+ }
+
+ void BeginWait(KThreadQueue* queue);
+ void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
+ void EndWait(ResultCode wait_result_);
+ void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
+
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
}
@@ -667,7 +702,6 @@ private:
KAffinityMask physical_affinity_mask{};
u64 thread_id{};
std::atomic<s64> cpu_time{};
- KSynchronizationObject* synced_object{};
VAddr address_key{};
KProcess* parent{};
VAddr kernel_stack_top{};
@@ -677,13 +711,14 @@ private:
s64 schedule_count{};
s64 last_scheduled_tick{};
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
- KThreadQueue* sleeping_queue{};
+ KThreadQueue* wait_queue{};
WaiterList waiter_list{};
WaiterList pinned_waiter_list{};
KThread* lock_owner{};
u32 address_key_value{};
u32 suspend_request_flags{};
u32 suspend_allowed_flags{};
+ s32 synced_index{};
ResultCode wait_result{ResultSuccess};
s32 base_priority{};
s32 physical_ideal_core_id{};
@@ -708,6 +743,7 @@ private:
// For emulation
std::shared_ptr<Common::Fiber> host_context{};
+ bool is_single_core{};
// For debugging
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
@@ -752,4 +788,20 @@ public:
}
};
+class KScopedDisableDispatch {
+public:
+ [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
+ // If we are shutting down the kernel, none of this is relevant anymore.
+ if (kernel.IsShuttingDown()) {
+ return;
+ }
+ GetCurrentThread(kernel).DisableDispatch();
+ }
+
+ ~KScopedDisableDispatch();
+
+private:
+ KernelCore& kernel;
+};
+
} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/k_thread_queue.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/time_manager.h"
+
+namespace Kernel {
+
+void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
+ [[maybe_unused]] KSynchronizationObject* signaled_object,
+ [[maybe_unused]] ResultCode wait_result) {}
+
+void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
+ // Set the thread's wait result.
+ waiting_thread->SetWaitResult(wait_result);
+
+ // Set the thread as runnable.
+ waiting_thread->SetState(ThreadState::Runnable);
+
+ // Clear the thread's wait queue.
+ waiting_thread->ClearWaitQueue();
+
+ // Cancel the thread task.
+ kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
+}
+
+void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task) {
+ // Set the thread's wait result.
+ waiting_thread->SetWaitResult(wait_result);
+
+ // Set the thread as runnable.
+ waiting_thread->SetState(ThreadState::Runnable);
+
+ // Clear the thread's wait queue.
+ waiting_thread->ClearWaitQueue();
+
+ // Cancel the thread task.
+ if (cancel_timer_task) {
+ kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
+ }
+}
+
+void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
+ [[maybe_unused]] ResultCode wait_result) {}
+
+} // 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 @@
#pragma once
+#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
@@ -11,71 +12,24 @@ namespace Kernel {
class KThreadQueue {
public:
explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {}
+ virtual ~KThreadQueue() = default;
- bool IsEmpty() const {
- return wait_list.empty();
- }
-
- KThread::WaiterList::iterator begin() {
- return wait_list.begin();
- }
- KThread::WaiterList::iterator end() {
- return wait_list.end();
- }
-
- bool SleepThread(KThread* t) {
- KScopedSchedulerLock sl{kernel};
-
- // If the thread needs terminating, don't enqueue it.
- if (t->IsTerminationRequested()) {
- return false;
- }
-
- // Set the thread's queue and mark it as waiting.
- t->SetSleepingQueue(this);
- t->SetState(ThreadState::Waiting);
-
- // Add the thread to the queue.
- wait_list.push_back(*t);
-
- return true;
- }
-
- void WakeupThread(KThread* t) {
- KScopedSchedulerLock sl{kernel};
-
- // Remove the thread from the queue.
- wait_list.erase(wait_list.iterator_to(*t));
-
- // Mark the thread as no longer sleeping.
- t->SetState(ThreadState::Runnable);
- t->SetSleepingQueue(nullptr);
- }
-
- KThread* WakeupFrontThread() {
- KScopedSchedulerLock sl{kernel};
-
- if (wait_list.empty()) {
- return nullptr;
- } else {
- // Remove the thread from the queue.
- auto it = wait_list.begin();
- KThread* thread = std::addressof(*it);
- wait_list.erase(it);
-
- ASSERT(thread->GetState() == ThreadState::Waiting);
-
- // Mark the thread as no longer sleeping.
- thread->SetState(ThreadState::Runnable);
- thread->SetSleepingQueue(nullptr);
-
- return thread;
- }
- }
+ virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
+ ResultCode wait_result);
+ virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
+ virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
+ bool cancel_timer_task);
private:
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
+class KThreadQueueWithoutEndWait : public KThreadQueue {
+public:
+ explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
+
+ void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
+};
+
} // 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 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
+#include "common/scope_exit.h"
#include "common/thread.h"
#include "common/thread_worker.h"
#include "core/arm/arm_interface.h"
@@ -83,12 +84,16 @@ struct KernelCore::Impl {
}
void InitializeCores() {
- for (auto& core : cores) {
- core.Initialize(current_process->Is64BitProcess());
+ for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
+ cores[core_id].Initialize(current_process->Is64BitProcess());
+ system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
void Shutdown() {
+ is_shutting_down.store(true, std::memory_order_relaxed);
+ SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
+
process_list.clear();
// Close all open server ports.
@@ -123,15 +128,6 @@ struct KernelCore::Impl {
next_user_process_id = KProcess::ProcessIDMin;
next_thread_id = 1;
- for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- if (suspend_threads[core_id]) {
- suspend_threads[core_id]->Close();
- suspend_threads[core_id] = nullptr;
- }
-
- schedulers[core_id].reset();
- }
-
cores.clear();
global_handle_table->Finalize();
@@ -159,6 +155,16 @@ struct KernelCore::Impl {
CleanupObject(time_shared_mem);
CleanupObject(system_resource_limit);
+ for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
+ if (suspend_threads[core_id]) {
+ suspend_threads[core_id]->Close();
+ suspend_threads[core_id] = nullptr;
+ }
+
+ schedulers[core_id]->Finalize();
+ schedulers[core_id].reset();
+ }
+
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
@@ -245,13 +251,11 @@ struct KernelCore::Impl {
KScopedSchedulerLock lock(kernel);
global_scheduler_context->PreemptThreads();
}
- const auto time_interval = std::chrono::nanoseconds{
- Core::Timing::msToCycles(std::chrono::milliseconds(10))};
+ const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
});
- const auto time_interval =
- std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))};
+ const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
}
@@ -267,14 +271,6 @@ struct KernelCore::Impl {
void MakeCurrentProcess(KProcess* process) {
current_process = process;
- if (process == nullptr) {
- return;
- }
-
- const u32 core_id = GetCurrentHostThreadID();
- if (core_id < Core::Hardware::NUM_CPU_CORES) {
- system.Memory().SetCurrentPageTable(*process, core_id);
- }
}
static inline thread_local u32 host_thread_id = UINT32_MAX;
@@ -300,15 +296,16 @@ struct KernelCore::Impl {
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
auto make_thread = [this]() {
- std::unique_ptr<KThread> thread = std::make_unique<KThread>(system.Kernel());
+ std::lock_guard lk(dummy_thread_lock);
+ auto& thread = dummy_threads.emplace_back(std::make_unique<KThread>(system.Kernel()));
KAutoObject::Create(thread.get());
ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
- return thread;
+ return thread.get();
};
- thread_local auto thread = make_thread();
- return thread.get();
+ thread_local KThread* saved_thread = make_thread();
+ return saved_thread;
}
/// Registers a CPU core thread by allocating a host thread ID for it
@@ -343,7 +340,16 @@ struct KernelCore::Impl {
is_phantom_mode_for_singlecore = value;
}
+ bool IsShuttingDown() const {
+ return is_shutting_down.load(std::memory_order_relaxed);
+ }
+
KThread* GetCurrentEmuThread() {
+ // If we are shutting down the kernel, none of this is relevant anymore.
+ if (IsShuttingDown()) {
+ return {};
+ }
+
const auto thread_id = GetCurrentHostThreadID();
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
@@ -695,6 +701,12 @@ struct KernelCore::Impl {
return port;
}
+ std::mutex server_ports_lock;
+ std::mutex server_sessions_lock;
+ std::mutex registered_objects_lock;
+ std::mutex registered_in_use_objects_lock;
+ std::mutex dummy_thread_lock;
+
std::atomic<u32> next_object_id{0};
std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
@@ -725,10 +737,6 @@ struct KernelCore::Impl {
std::unordered_set<KServerSession*> server_sessions;
std::unordered_set<KAutoObject*> registered_objects;
std::unordered_set<KAutoObject*> registered_in_use_objects;
- std::mutex server_ports_lock;
- std::mutex server_sessions_lock;
- std::mutex registered_objects_lock;
- std::mutex registered_in_use_objects_lock;
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
std::vector<Kernel::PhysicalCore> cores;
@@ -753,7 +761,11 @@ struct KernelCore::Impl {
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
+ // Specifically tracked to be automatically destroyed with kernel
+ std::vector<std::unique_ptr<KThread>> dummy_threads;
+
bool is_multicore{};
+ std::atomic_bool is_shutting_down{};
bool is_phantom_mode_for_singlecore{};
u32 single_core_thread_id{};
@@ -839,16 +851,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
return impl->cores[id];
}
+size_t KernelCore::CurrentPhysicalCoreIndex() const {
+ const u32 core_id = impl->GetCurrentHostThreadID();
+ if (core_id >= Core::Hardware::NUM_CPU_CORES) {
+ return Core::Hardware::NUM_CPU_CORES - 1;
+ }
+ return core_id;
+}
+
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
- u32 core_id = impl->GetCurrentHostThreadID();
- ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
- return impl->cores[core_id];
+ return impl->cores[CurrentPhysicalCoreIndex()];
}
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
- u32 core_id = impl->GetCurrentHostThreadID();
- ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
- return impl->cores[core_id];
+ return impl->cores[CurrentPhysicalCoreIndex()];
}
Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -1051,6 +1067,9 @@ void KernelCore::Suspend(bool in_suspention) {
impl->suspend_threads[core_id]->SetState(state);
impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
ThreadWaitReasonForDebugging::Suspended);
+ if (!should_suspend) {
+ impl->suspend_threads[core_id]->DisableDispatch();
+ }
}
}
}
@@ -1059,19 +1078,21 @@ bool KernelCore::IsMulticore() const {
return impl->is_multicore;
}
+bool KernelCore::IsShuttingDown() const {
+ return impl->IsShuttingDown();
+}
+
void KernelCore::ExceptionalExit() {
exception_exited = true;
Suspend(true);
}
void KernelCore::EnterSVCProfile() {
- std::size_t core = impl->GetCurrentHostThreadID();
- impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
+ impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
}
void KernelCore::ExitSVCProfile() {
- std::size_t core = impl->GetCurrentHostThreadID();
- MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
+ MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
}
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index d2ceae950..b9b423908 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -53,6 +53,7 @@ class KSharedMemoryInfo;
class KThread;
class KTransferMemory;
class KWritableEvent;
+class KCodeMemory;
class PhysicalCore;
class ServiceThread;
class Synchronization;
@@ -148,6 +149,9 @@ public:
/// Gets the an instance of the respective physical CPU core.
const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
+ /// Gets the current physical core index for the running host thread.
+ std::size_t CurrentPhysicalCoreIndex() const;
+
/// Gets the sole instance of the Scheduler at the current running core.
Kernel::KScheduler* CurrentScheduler();
@@ -271,6 +275,8 @@ public:
bool IsMulticore() const;
+ bool IsShuttingDown() const;
+
void EnterSVCProfile();
void ExitSVCProfile();
@@ -326,6 +332,8 @@ public:
return slab_heap_container->transfer_memory;
} else if constexpr (std::is_same_v<T, KWritableEvent>) {
return slab_heap_container->writeable_event;
+ } else if constexpr (std::is_same_v<T, KCodeMemory>) {
+ return slab_heap_container->code_memory;
}
}
@@ -377,6 +385,7 @@ private:
KSlabHeap<KThread> thread;
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KWritableEvent> writeable_event;
+ KSlabHeap<KCodeMemory> code_memory;
};
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:
void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
private:
- std::vector<std::thread> threads;
+ std::vector<std::jthread> threads;
std::queue<std::function<void()>> requests;
std::mutex queue_mutex;
- std::condition_variable condition;
+ std::condition_variable_any condition;
const std::string service_name;
- bool stop{};
};
ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
: service_name{name} {
- for (std::size_t i = 0; i < num_threads; ++i)
- threads.emplace_back([this, &kernel] {
+ for (std::size_t i = 0; i < num_threads; ++i) {
+ threads.emplace_back([this, &kernel](std::stop_token stop_token) {
Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
// Wait for first request before trying to acquire a render context
{
std::unique_lock lock{queue_mutex};
- condition.wait(lock, [this] { return stop || !requests.empty(); });
+ condition.wait(lock, stop_token, [this] { return !requests.empty(); });
+ }
+
+ if (stop_token.stop_requested()) {
+ return;
}
kernel.RegisterHostThread();
@@ -52,10 +55,16 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
{
std::unique_lock lock{queue_mutex};
- condition.wait(lock, [this] { return stop || !requests.empty(); });
- if (stop || requests.empty()) {
+ condition.wait(lock, stop_token, [this] { return !requests.empty(); });
+
+ if (stop_token.stop_requested()) {
return;
}
+
+ if (requests.empty()) {
+ continue;
+ }
+
task = std::move(requests.front());
requests.pop();
}
@@ -63,6 +72,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
task();
}
});
+ }
}
void ServiceThread::Impl::QueueSyncRequest(KSession& session,
@@ -88,12 +98,9 @@ void ServiceThread::Impl::QueueSyncRequest(KSession& session,
}
ServiceThread::Impl::~Impl() {
- {
- std::unique_lock lock{queue_mutex};
- stop = true;
- }
condition.notify_all();
- for (std::thread& thread : threads) {
+ for (auto& thread : threads) {
+ thread.request_stop();
thread.join();
}
}
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 @@
#include "core/core_timing.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_memory_block.h"
@@ -31,6 +32,7 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
@@ -307,26 +309,29 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
-
auto& kernel = system.Kernel();
+ // Create the wait queue.
+ KThreadQueue wait_queue(kernel);
+
+ // Get the client session from its handle.
+ KScopedAutoObject session =
+ kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
+ R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+
+ LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
+
auto thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLock lock(kernel);
- thread->SetState(ThreadState::Waiting);
- thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
-
- {
- KScopedAutoObject session =
- kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
- R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
- LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
- session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
- }
+
+ // This is a synchronous request, so we should wait for our request to complete.
+ GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
+ GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
+ session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
}
- KSynchronizationObject* dummy{};
- return thread->GetWaitResult(std::addressof(dummy));
+ return thread->GetWaitResult();
}
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -873,7 +878,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
const u64 thread_ticks = current_thread->GetCpuTime();
out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
- } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
+ } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
}
@@ -887,7 +892,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
return ResultInvalidHandle;
}
- if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id != system.CurrentCoreIndex()) {
+ if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
+ info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
return ResultInvalidCombination;
}
@@ -1169,6 +1175,8 @@ static u32 GetCurrentProcessorNumber32(Core::System& system) {
return GetCurrentProcessorNumber(system);
}
+namespace {
+
constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
switch (perm) {
case Svc::MemoryPermission::Read:
@@ -1179,10 +1187,40 @@ constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
}
}
-constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
+[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
}
+constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
+ switch (perm) {
+ case Svc::MemoryPermission::None:
+ case Svc::MemoryPermission::Read:
+ case Svc::MemoryPermission::ReadWrite:
+ case Svc::MemoryPermission::ReadExecute:
+ return true;
+ default:
+ return false;
+ }
+}
+
+constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::ReadWrite;
+}
+
+constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
+}
+
+constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::None;
+}
+
+constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::None;
+}
+
+} // Anonymous namespace
+
static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
u64 size, Svc::MemoryPermission map_perm) {
LOG_TRACE(Kernel_SVC,
@@ -1262,6 +1300,223 @@ static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle,
return UnmapSharedMemory(system, shmem_handle, address, size);
}
+static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle,
+ VAddr address, u64 size, Svc::MemoryPermission perm) {
+ LOG_TRACE(Kernel_SVC,
+ "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
+ process_handle, address, size, perm);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Validate the memory permission.
+ R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Get the process from its handle.
+ KScopedAutoObject process =
+ system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
+ R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
+
+ // Validate that the address is in range.
+ auto& page_table = process->PageTable();
+ R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Set the memory permission.
+ return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
+}
+
+static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
+ LOG_TRACE(Kernel_SVC,
+ "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
+ dst_address, process_handle, src_address, size);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+
+ // Get the processes.
+ KProcess* dst_process = system.CurrentProcess();
+ KScopedAutoObject src_process =
+ dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
+ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+
+ // Get the page tables.
+ auto& dst_pt = dst_process->PageTable();
+ auto& src_pt = src_process->PageTable();
+
+ // Validate that the mapping is in range.
+ R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
+ ResultInvalidMemoryRegion);
+
+ // Create a new page group.
+ KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
+ KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
+
+ // Map the group.
+ R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
+ KMemoryPermission::UserReadWrite));
+
+ return ResultSuccess;
+}
+
+static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
+ LOG_TRACE(Kernel_SVC,
+ "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
+ dst_address, process_handle, src_address, size);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+
+ // Get the processes.
+ KProcess* dst_process = system.CurrentProcess();
+ KScopedAutoObject src_process =
+ dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
+ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+
+ // Get the page tables.
+ auto& dst_pt = dst_process->PageTable();
+ auto& src_pt = src_process->PageTable();
+
+ // Validate that the mapping is in range.
+ R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
+ ResultInvalidMemoryRegion);
+
+ // Unmap the memory.
+ R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
+
+ return ResultSuccess;
+}
+
+static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
+ LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
+ static_cast<void*>(out), address, size);
+ // Get kernel instance.
+ auto& kernel = system.Kernel();
+
+ // Validate address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Create the code memory.
+
+ KCodeMemory* code_mem = KCodeMemory::Create(kernel);
+ R_UNLESS(code_mem != nullptr, ResultOutOfResource);
+
+ // Verify that the region is in range.
+ R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
+ ResultInvalidCurrentMemory);
+
+ // Initialize the code memory.
+ R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
+
+ // Register the code memory.
+ KCodeMemory::Register(kernel, code_mem);
+
+ // Add the code memory to the handle table.
+ R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
+
+ code_mem->Close();
+
+ return ResultSuccess;
+}
+
+static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
+ VAddr address, size_t size, Svc::MemoryPermission perm) {
+
+ LOG_TRACE(Kernel_SVC,
+ "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
+ "permission=0x{:X}",
+ code_memory_handle, operation, address, size, perm);
+
+ // Validate the address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Get the code memory from its handle.
+ KScopedAutoObject code_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
+ R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
+
+ // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
+ // This enables homebrew usage of these SVCs for JIT.
+
+ // Perform the operation.
+ switch (static_cast<CodeMemoryOperation>(operation)) {
+ case CodeMemoryOperation::Map: {
+ // Check that the region is in range.
+ R_UNLESS(
+ system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Map the memory.
+ R_TRY(code_mem->Map(address, size));
+ } break;
+ case CodeMemoryOperation::Unmap: {
+ // Check that the region is in range.
+ R_UNLESS(
+ system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Unmap the memory.
+ R_TRY(code_mem->Unmap(address, size));
+ } break;
+ case CodeMemoryOperation::MapToOwner: {
+ // Check that the region is in range.
+ R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
+ KMemoryState::GeneratedCode),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Map the memory to its owner.
+ R_TRY(code_mem->MapToOwner(address, size, perm));
+ } break;
+ case CodeMemoryOperation::UnmapFromOwner: {
+ // Check that the region is in range.
+ R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
+ KMemoryState::GeneratedCode),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Unmap the memory from its owner.
+ R_TRY(code_mem->UnmapFromOwner(address, size));
+ } break;
+ default:
+ return ResultInvalidEnumValue;
+ }
+
+ return ResultSuccess;
+}
+
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
@@ -1459,10 +1714,14 @@ static void ExitProcess32(Core::System& system) {
ExitProcess(system);
}
-static constexpr bool IsValidVirtualCoreId(int32_t core_id) {
+namespace {
+
+constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
}
+} // Anonymous namespace
+
/// Creates a new thread
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
VAddr stack_bottom, u32 priority, s32 core_id) {
@@ -1846,7 +2105,9 @@ static ResultCode ResetSignal32(Core::System& system, Handle handle) {
return ResetSignal(system, handle);
}
-static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
+namespace {
+
+constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
switch (perm) {
case MemoryPermission::None:
case MemoryPermission::Read:
@@ -1857,6 +2118,8 @@ static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
}
}
+} // Anonymous namespace
+
/// Creates a TransferMemory object
static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
MemoryPermission map_perm) {
@@ -2548,8 +2811,8 @@ static const FunctionDef SVC_Table_64[] = {
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
- {0x4B, nullptr, "CreateCodeMemory"},
- {0x4C, nullptr, "ControlCodeMemory"},
+ {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"},
+ {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"},
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
@@ -2588,9 +2851,9 @@ static const FunctionDef SVC_Table_64[] = {
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},
- {0x73, nullptr, "SetProcessMemoryPermission"},
- {0x74, nullptr, "MapProcessMemory"},
- {0x75, nullptr, "UnmapProcessMemory"},
+ {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
+ {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"},
+ {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"},
{0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
{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) {
.raw);
}
+// Used by MapProcessMemory and UnmapProcessMemory
+template <ResultCode func(Core::System&, u64, u32, u64, u64)>
+void SvcWrap64(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
+ Param(system, 2), Param(system, 3))
+ .raw);
+}
+
+// Used by ControlCodeMemory
+template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
+void SvcWrap64(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
+ static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
+ static_cast<Svc::MemoryPermission>(Param(system, 4)))
+ .raw);
+}
+
template <ResultCode func(Core::System&, u32*)>
void SvcWrap64(Core::System& system) {
u32 param = 0;
@@ -301,6 +318,16 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
+// Used by CreateCodeMemory
+template <ResultCode func(Core::System&, Handle*, u64, u64)>
+void SvcWrap64(Core::System& system) {
+ u32 param_1 = 0;
+ const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
+}
+
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap64(Core::System& system) {
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 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/time_manager.h"
@@ -15,7 +16,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
- thread->Wakeup();
+ {
+ KScopedSchedulerLock sl(system.Kernel());
+ thread->OnTimer();
+ }
});
}
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 @@
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/apm/apm_interface.h"
#include "core/hle/service/bcat/backend/backend.h"
+#include "core/hle/service/caps/caps.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -298,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
{91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
{100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
{110, nullptr, "SetApplicationAlbumUserData"},
- {120, nullptr, "SaveCurrentScreenshot"},
+ {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
{130, nullptr, "SetRecordVolumeMuted"},
{1000, nullptr, "GetDebugStorageChannel"},
};
@@ -579,6 +580,17 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestCo
rb.Push(ResultSuccess);
}
+void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto album_report_option = rp.PopEnum<Capture::AlbumReportOption>();
+
+ LOG_WARNING(Service_AM, "(STUBBED) called. album_report_option={}", album_report_option);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
AppletMessageQueue::AppletMessageQueue(Core::System& system)
: service_context{system, "AppletMessageQueue"} {
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:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx);
+ void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx);
enum class ScreenshotPermission : u32 {
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 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/controller.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hid/hid_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_controller.h"
@@ -25,7 +28,7 @@ namespace Service::AM::Applets {
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
- HID::Controller_NPad::NpadStyleSet npad_style_set;
+ Core::HID::NpadStyleTag npad_style_set;
npad_style_set.raw = private_arg.style_set;
return {
@@ -243,19 +246,11 @@ void Controller::Execute() {
void Controller::ConfigurationComplete() {
ControllerSupportResultInfo result_info{};
- const auto& players = Settings::values.players.GetValue();
-
// If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
// Otherwise, only count connected players from P1-P8.
- result_info.player_count =
- is_single_mode
- ? 1
- : static_cast<s8>(std::count_if(players.begin(), players.end() - 2,
- [](const auto& player) { return player.connected; }));
-
- result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance(
- players.begin(), std::find_if(players.begin(), players.end(),
- [](const auto& player) { return player.connected; })));
+ result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount();
+
+ result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
result_info.result = 0;
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 {
class System;
}
+namespace Core::HID {
+enum class NpadStyleSet : u32;
+}
+
namespace Service::AM::Applets {
using IdentificationColor = std::array<u8, 4>;
@@ -52,7 +56,7 @@ struct ControllerSupportArgPrivate {
bool flag_1{};
ControllerSupportMode mode{};
ControllerSupportCaller caller{};
- u32 style_set{};
+ Core::HID::NpadStyleSet style_set{};
u32 joy_hold_type{};
};
static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 7320b1c0f..134ac1ee2 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -231,7 +231,7 @@ void AppletManager::SetDefaultAppletFrontendSet() {
void AppletManager::SetDefaultAppletsIfMissing() {
if (frontend.controller == nullptr) {
frontend.controller =
- std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
+ std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
}
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:
bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
std::vector<opus_int16>& output, u64* out_performance_time) const {
- const auto start_time = std::chrono::high_resolution_clock::now();
+ const auto start_time = std::chrono::steady_clock::now();
const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
if (sizeof(OpusPacketHeader) > input.size()) {
LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
@@ -135,7 +135,7 @@ private:
return false;
}
- const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
+ const auto end_time = std::chrono::steady_clock::now() - start_time;
sample_count = out_sample_count;
consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
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 {
Orientation3 = 3,
};
-enum class AlbumReportOption {
+enum class AlbumReportOption : s32 {
Disable = 0,
Enable = 1,
};
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 {
class IFriendService final : public ServiceFramework<IFriendService> {
public:
- explicit IFriendService(Core::System& system_) : ServiceFramework{system_, "IFriendService"} {
+ explicit IFriendService(Core::System& system_)
+ : ServiceFramework{system_, "IFriendService"}, service_context{system, "IFriendService"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "GetCompletionEvent"},
+ {0, &IFriendService::GetCompletionEvent, "GetCompletionEvent"},
{1, nullptr, "Cancel"},
{10100, nullptr, "GetFriendListIds"},
{10101, &IFriendService::GetFriendList, "GetFriendList"},
@@ -109,6 +110,12 @@ public:
// clang-format on
RegisterHandlers(functions);
+
+ completion_event = service_context.CreateEvent("IFriendService:CompletionEvent");
+ }
+
+ ~IFriendService() override {
+ service_context.CloseEvent(completion_event);
}
private:
@@ -129,6 +136,14 @@ private:
};
static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
+ void GetCompletionEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Friend, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(completion_event->GetReadableEvent());
+ }
+
void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) {
// This is safe to stub, as there should be no adverse consequences from reporting no
// blocked users.
@@ -179,6 +194,10 @@ private:
rb.Push<u32>(0); // Friend count
// TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
}
+
+ KernelHelpers::ServiceContext service_context;
+
+ Kernel::KEvent* completion_event;
};
class INotificationService final : public ServiceFramework<INotificationService> {
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index a08dc9758..b24d469cf 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -8,6 +8,7 @@
#include "core/hle/service/glue/bgtc.h"
#include "core/hle/service/glue/ectx.h"
#include "core/hle/service/glue/glue.h"
+#include "core/hle/service/glue/notif.h"
namespace Service::Glue {
@@ -24,6 +25,9 @@ void InstallInterfaces(Core::System& system) {
// Error Context
std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager());
+
+ // Notification Services for application
+ std::make_shared<NOTIF_A>(system)->InstallAsService(system.ServiceManager());
}
} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/glue/notif.h"
+
+namespace Service::Glue {
+
+NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {500, nullptr, "RegisterAlarmSetting"},
+ {510, nullptr, "UpdateAlarmSetting"},
+ {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
+ {530, nullptr, "LoadApplicationParameter"},
+ {540, nullptr, "DeleteAlarmSetting"},
+ {1000, &NOTIF_A::Initialize, "Initialize"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+NOTIF_A::~NOTIF_A() = default;
+
+void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
+ // Returns an array of AlarmSetting
+ constexpr s32 alarm_count = 0;
+
+ LOG_WARNING(Service_NOTIF, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(alarm_count);
+}
+
+void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NOTIF, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Glue {
+
+class NOTIF_A final : public ServiceFramework<NOTIF_A> {
+public:
+ explicit NOTIF_A(Core::System& system_);
+ ~NOTIF_A() override;
+
+private:
+ void ListAlarmSettings(Kernel::HLERequestContext& ctx);
+ void Initialize(Kernel::HLERequestContext& ctx);
+};
+
+} // 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 @@
#include "common/settings.h"
#include "core/core_timing.h"
+#include "core/hid/emulated_console.h"
+#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/console_sixaxis.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
-Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_)
- : ControllerBase{system_} {}
+Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_)
+ : ControllerBase{hid_core_} {
+ console = hid_core.GetEmulatedConsole();
+}
+
Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
void Controller_ConsoleSixAxis::OnInit() {}
@@ -19,44 +24,31 @@ void Controller_ConsoleSixAxis::OnRelease() {}
void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- seven_six_axis.header.timestamp = core_timing.GetCPUTicks();
- seven_six_axis.header.total_entry_count = 17;
-
if (!IsControllerActivated() || !is_transfer_memory_set) {
- seven_six_axis.header.entry_count = 0;
- seven_six_axis.header.last_entry_index = 0;
+ seven_sixaxis_lifo.buffer_count = 0;
+ seven_sixaxis_lifo.buffer_tail = 0;
return;
}
- seven_six_axis.header.entry_count = 16;
-
- const auto& last_entry =
- seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
- seven_six_axis.header.last_entry_index = (seven_six_axis.header.last_entry_index + 1) % 17;
- auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
- cur_entry.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number2 = cur_entry.sampling_number;
+ const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
+ next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
// Try to read sixaxis sensor states
- MotionDevice motion_device{};
- const auto& device = motions[0];
- if (device) {
- std::tie(motion_device.accel, motion_device.gyro, motion_device.rotation,
- motion_device.orientation, motion_device.quaternion) = device->GetStatus();
- console_six_axis.is_seven_six_axis_sensor_at_rest = motion_device.gyro.Length2() < 0.0001f;
- }
+ const auto motion_status = console->GetMotion();
+
+ console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
- cur_entry.accel = motion_device.accel;
+ next_seven_sixaxis_state.accel = motion_status.accel;
// Zero gyro values as they just mess up with the camera
// Note: Probably a correct sensivity setting must be set
- cur_entry.gyro = {};
- cur_entry.quaternion = {
+ next_seven_sixaxis_state.gyro = {};
+ next_seven_sixaxis_state.quaternion = {
{
- motion_device.quaternion.xyz.y,
- motion_device.quaternion.xyz.x,
- -motion_device.quaternion.w,
+ motion_status.quaternion.xyz.y,
+ motion_status.quaternion.xyz.x,
+ -motion_status.quaternion.w,
},
- -motion_device.quaternion.xyz.z,
+ -motion_status.quaternion.xyz.z,
};
console_six_axis.sampling_number++;
@@ -67,14 +59,8 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
// Update console six axis shared memory
std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
// Update seven six axis transfer memory
- std::memcpy(transfer_memory, &seven_six_axis, sizeof(seven_six_axis));
-}
-
-void Controller_ConsoleSixAxis::OnLoadInputDevices() {
- const auto player = Settings::values.players.GetValue()[0];
- std::transform(player.motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
- player.motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions.begin(),
- Input::CreateDevice<Input::MotionDevice>);
+ seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
+ std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo));
}
void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
@@ -83,8 +69,7 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
}
void Controller_ConsoleSixAxis::ResetTimestamp() {
- auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
- cur_entry.sampling_number = 0;
- cur_entry.sampling_number2 = 0;
+ seven_sixaxis_lifo.buffer_count = 0;
+ seven_sixaxis_lifo.buffer_tail = 0;
}
} // 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 @@
#pragma once
#include <array>
-#include "common/bit_field.h"
+
#include "common/common_types.h"
#include "common/quaternion.h"
-#include "core/frontend/input.h"
+#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/ring_lifo.h"
+
+namespace Core::HID {
+class EmulatedConsole;
+} // namespace Core::HID
namespace Service::HID {
class Controller_ConsoleSixAxis final : public ControllerBase {
public:
- explicit Controller_ConsoleSixAxis(Core::System& system_);
+ explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
~Controller_ConsoleSixAxis() override;
// Called when the controller is initialized
@@ -26,9 +31,6 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
// Called on InitializeSevenSixAxisSensor
void SetTransferMemoryPointer(u8* t_mem);
@@ -38,43 +40,31 @@ public:
private:
struct SevenSixAxisState {
INSERT_PADDING_WORDS(4); // unused
- s64_le sampling_number{};
- s64_le sampling_number2{};
+ s64 sampling_number{};
u64 unknown{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Quaternion<f32> quaternion{};
};
- static_assert(sizeof(SevenSixAxisState) == 0x50, "SevenSixAxisState is an invalid size");
-
- struct SevenSixAxisMemory {
- CommonHeader header{};
- std::array<SevenSixAxisState, 0x21> sevensixaxis_states{};
- };
- static_assert(sizeof(SevenSixAxisMemory) == 0xA70, "SevenSixAxisMemory is an invalid size");
+ static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
+ // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSharedMemory {
- u64_le sampling_number{};
+ u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
+ INSERT_PADDING_BYTES(4); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
};
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
- struct MotionDevice {
- Common::Vec3f accel;
- Common::Vec3f gyro;
- Common::Vec3f rotation;
- std::array<Common::Vec3f, 3> orientation;
- Common::Quaternion<f32> quaternion;
- };
+ Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
+ static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
- using MotionArray =
- std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>;
- MotionArray motions;
+ Core::HID::EmulatedConsole* console;
u8* transfer_memory = nullptr;
bool is_transfer_memory_set = false;
ConsoleSharedMemory console_six_axis{};
- SevenSixAxisMemory seven_six_axis{};
+ SevenSixAxisState next_seven_sixaxis_state{};
};
} // 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 @@
namespace Service::HID {
-ControllerBase::ControllerBase(Core::System& system_) : system(system_) {}
+ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
ControllerBase::~ControllerBase() = default;
void ControllerBase::ActivateController() {
if (is_activated) {
- OnRelease();
+ return;
}
is_activated = true;
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 {
class CoreTiming;
}
-namespace Core {
-class System;
+namespace Core::HID {
+class HIDCore;
}
namespace Service::HID {
class ControllerBase {
public:
- explicit ControllerBase(Core::System& system_);
+ explicit ControllerBase(Core::HID::HIDCore& hid_core_);
virtual ~ControllerBase();
// Called when the controller is initialized
@@ -35,26 +35,17 @@ public:
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {}
- // Called when input devices should be loaded
- virtual void OnLoadInputDevices() = 0;
-
void ActivateController();
void DeactivateController();
bool IsControllerActivated() const;
+ static const std::size_t hid_entry_count = 17;
+
protected:
bool is_activated{false};
- struct CommonHeader {
- s64_le timestamp;
- s64_le total_entry_count;
- s64_le last_entry_index;
- s64_le entry_count;
- };
- static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
-
- Core::System& system;
+ Core::HID::HIDCore& hid_core;
};
} // 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 @@
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
namespace Service::HID {
+constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
-constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
-[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
-enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
+Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_)
+ : ControllerBase{hid_core_} {
+ controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
+}
-Controller_DebugPad::Controller_DebugPad(Core::System& system_) : ControllerBase{system_} {}
Controller_DebugPad::~Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}
@@ -23,63 +27,29 @@ void Controller_DebugPad::OnRelease() {}
void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetCPUTicks();
- shared_memory.header.total_entry_count = 17;
-
if (!IsControllerActivated()) {
- shared_memory.header.entry_count = 0;
- shared_memory.header.last_entry_index = 0;
+ debug_pad_lifo.buffer_count = 0;
+ debug_pad_lifo.buffer_tail = 0;
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
return;
}
- shared_memory.header.entry_count = 16;
- const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
- shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
- auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
-
- cur_entry.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number2 = cur_entry.sampling_number;
+ const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state;
+ next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.debug_pad_enabled) {
- cur_entry.attribute.connected.Assign(1);
- auto& pad = cur_entry.pad_state;
+ next_state.attribute.connected.Assign(1);
- using namespace Settings::NativeButton;
- pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
- pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
- pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
- pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
- pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
- pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
- pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
- pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
- pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
- pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
- pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
- pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
- pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
- pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
+ const auto& button_state = controller->GetDebugPadButtons();
+ const auto& stick_state = controller->GetSticks();
- const auto [stick_l_x_f, stick_l_y_f] =
- analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
- const auto [stick_r_x_f, stick_r_y_f] =
- analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
- cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
- cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
- cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
- cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+ next_state.pad_state = button_state;
+ next_state.l_stick = stick_state.left;
+ next_state.r_stick = stick_state.right;
}
- std::memcpy(data, &shared_memory, sizeof(SharedMemory));
+ debug_pad_lifo.WriteNextEntry(next_state);
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
}
-void Controller_DebugPad::OnLoadInputDevices() {
- std::transform(Settings::values.debug_pad_buttons.begin(),
- Settings::values.debug_pad_buttons.begin() +
- Settings::NativeButton::NUM_BUTTONS_HID,
- buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
- std::transform(Settings::values.debug_pad_analogs.begin(),
- Settings::values.debug_pad_analogs.end(), analogs.begin(),
- Input::CreateDevice<Input::AnalogDevice>);
-}
} // 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 @@
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/settings.h"
#include "common/swap.h"
-#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/ring_lifo.h"
+
+namespace Core::HID {
+class EmulatedController;
+struct DebugPadButton;
+struct AnalogStickState;
+} // namespace Core::HID
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
public:
- explicit Controller_DebugPad(Core::System& system_);
+ explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_);
~Controller_DebugPad() override;
// Called when the controller is initialized
@@ -28,66 +33,31 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
private:
- struct AnalogStick {
- s32_le x;
- s32_le y;
- };
- static_assert(sizeof(AnalogStick) == 0x8);
-
- struct PadState {
- union {
- u32_le raw{};
- BitField<0, 1, u32> a;
- BitField<1, 1, u32> b;
- BitField<2, 1, u32> x;
- BitField<3, 1, u32> y;
- BitField<4, 1, u32> l;
- BitField<5, 1, u32> r;
- BitField<6, 1, u32> zl;
- BitField<7, 1, u32> zr;
- BitField<8, 1, u32> plus;
- BitField<9, 1, u32> minus;
- BitField<10, 1, u32> d_left;
- BitField<11, 1, u32> d_up;
- BitField<12, 1, u32> d_right;
- BitField<13, 1, u32> d_down;
- };
- };
- static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
-
- struct Attributes {
+ // This is nn::hid::DebugPadAttribute
+ struct DebugPadAttribute {
union {
- u32_le raw{};
+ u32 raw{};
BitField<0, 1, u32> connected;
};
};
- static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
-
- struct PadStates {
- s64_le sampling_number;
- s64_le sampling_number2;
- Attributes attribute;
- PadState pad_state;
- AnalogStick r_stick;
- AnalogStick l_stick;
+ static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
+
+ // This is nn::hid::DebugPadState
+ struct DebugPadState {
+ s64 sampling_number;
+ DebugPadAttribute attribute;
+ Core::HID::DebugPadButton pad_state;
+ Core::HID::AnalogStickState r_stick;
+ Core::HID::AnalogStickState l_stick;
};
- static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state");
+ static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
- struct SharedMemory {
- CommonHeader header;
- std::array<PadStates, 17> pad_states;
- INSERT_PADDING_BYTES(0x138);
- };
- static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
- SharedMemory shared_memory{};
+ // This is nn::hid::detail::DebugPadLifo
+ Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
+ static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
+ DebugPadState next_state{};
- std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
- buttons;
- std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
- analogs;
+ Core::HID::EmulatedController* controller;
};
} // 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 @@
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
+#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/gesture.h"
namespace Service::HID {
@@ -23,16 +24,14 @@ constexpr f32 Square(s32 num) {
return static_cast<f32>(num * num);
}
-Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {}
+Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
+ console = hid_core.GetEmulatedConsole();
+}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {
- for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
- mouse_finger_id[id] = MAX_POINTS;
- keyboard_finger_id[id] = MAX_POINTS;
- udp_finger_id[id] = MAX_POINTS;
- }
- shared_memory.header.entry_count = 0;
+ gesture_lifo.buffer_count = 0;
+ gesture_lifo.buffer_tail = 0;
force_update = true;
}
@@ -40,50 +39,38 @@ void Controller_Gesture::OnRelease() {}
void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetCPUTicks();
- shared_memory.header.total_entry_count = 17;
-
if (!IsControllerActivated()) {
- shared_memory.header.entry_count = 0;
- shared_memory.header.last_entry_index = 0;
+ gesture_lifo.buffer_count = 0;
+ gesture_lifo.buffer_tail = 0;
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
return;
}
ReadTouchInput();
GestureProperties gesture = GetGestureProperties();
- f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) /
- (1000 * 1000 * 1000);
+ f32 time_difference =
+ static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
// Only update if necesary
if (!ShouldUpdateGesture(gesture, time_difference)) {
return;
}
- last_update_timestamp = shared_memory.header.timestamp;
+ last_update_timestamp = gesture_lifo.timestamp;
UpdateGestureSharedMemory(data, size, gesture, time_difference);
}
void Controller_Gesture::ReadTouchInput() {
- const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
- const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
- for (std::size_t id = 0; id < mouse_status.size(); ++id) {
- mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
- udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
- }
-
- if (Settings::values.use_touch_from_button) {
- const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
- for (std::size_t id = 0; id < mouse_status.size(); ++id) {
- keyboard_finger_id[id] =
- UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
- }
+ const auto touch_status = console->GetTouch();
+ for (std::size_t id = 0; id < fingers.size(); ++id) {
+ fingers[id] = touch_status[id];
}
}
bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
f32 time_difference) {
- const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ const auto& last_entry = GetLastGestureEntry();
if (force_update) {
force_update = false;
return true;
@@ -97,7 +84,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
}
// Update on press and hold event after 0.5 seconds
- if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 &&
+ if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
time_difference > press_delay) {
return enable_press_and_tap;
}
@@ -108,27 +95,19 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
GestureProperties& gesture,
f32 time_difference) {
- TouchType type = TouchType::Idle;
- Attribute attributes{};
+ GestureType type = GestureType::Idle;
+ GestureAttribute attributes{};
- const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
- shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
- auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
- if (shared_memory.header.entry_count < 16) {
- shared_memory.header.entry_count++;
- }
-
- cur_entry.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number2 = cur_entry.sampling_number;
-
- // Reset values to default
- cur_entry.delta = {};
- cur_entry.vel_x = 0;
- cur_entry.vel_y = 0;
- cur_entry.direction = Direction::None;
- cur_entry.rotation_angle = 0;
- cur_entry.scale = 0;
+ // Reset next state to default
+ next_state.sampling_number = last_entry.sampling_number + 1;
+ next_state.delta = {};
+ next_state.vel_x = 0;
+ next_state.vel_y = 0;
+ next_state.direction = GestureDirection::None;
+ next_state.rotation_angle = 0;
+ next_state.scale = 0;
if (gesture.active_points > 0) {
if (last_gesture.active_points == 0) {
@@ -141,46 +120,47 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
}
// Apply attributes
- cur_entry.detection_count = gesture.detection_count;
- cur_entry.type = type;
- cur_entry.attributes = attributes;
- cur_entry.pos = gesture.mid_point;
- cur_entry.point_count = static_cast<s32>(gesture.active_points);
- cur_entry.points = gesture.points;
+ next_state.detection_count = gesture.detection_count;
+ next_state.type = type;
+ next_state.attributes = attributes;
+ next_state.pos = gesture.mid_point;
+ next_state.point_count = static_cast<s32>(gesture.active_points);
+ next_state.points = gesture.points;
last_gesture = gesture;
- std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
+ gesture_lifo.WriteNextEntry(next_state);
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
}
-void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type,
- Attribute& attributes) {
+void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
+ GestureAttribute& attributes) {
const auto& last_entry = GetLastGestureEntry();
gesture.detection_count++;
- type = TouchType::Touch;
+ type = GestureType::Touch;
// New touch after cancel is not considered new
- if (last_entry.type != TouchType::Cancel) {
+ if (last_entry.type != GestureType::Cancel) {
attributes.is_new_touch.Assign(1);
enable_press_and_tap = true;
}
}
-void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type,
+void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
// Promote to pan type if touch moved
for (size_t id = 0; id < MAX_POINTS; id++) {
if (gesture.points[id] != last_gesture.points[id]) {
- type = TouchType::Pan;
+ type = GestureType::Pan;
break;
}
}
// Number of fingers changed cancel the last event and clear data
if (gesture.active_points != last_gesture.active_points) {
- type = TouchType::Cancel;
+ type = GestureType::Cancel;
enable_press_and_tap = false;
gesture.active_points = 0;
gesture.mid_point = {};
@@ -189,41 +169,41 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch
}
// Calculate extra parameters of panning
- if (type == TouchType::Pan) {
+ if (type == GestureType::Pan) {
UpdatePanEvent(gesture, last_gesture, type, time_difference);
return;
}
// Promote to press type
- if (last_entry.type == TouchType::Touch) {
- type = TouchType::Press;
+ if (last_entry.type == GestureType::Touch) {
+ type = GestureType::Press;
}
}
void Controller_Gesture::EndGesture(GestureProperties& gesture,
- GestureProperties& last_gesture_props, TouchType& type,
- Attribute& attributes, f32 time_difference) {
+ GestureProperties& last_gesture_props, GestureType& type,
+ GestureAttribute& attributes, f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
if (last_gesture_props.active_points != 0) {
switch (last_entry.type) {
- case TouchType::Touch:
+ case GestureType::Touch:
if (enable_press_and_tap) {
SetTapEvent(gesture, last_gesture_props, type, attributes);
return;
}
- type = TouchType::Cancel;
+ type = GestureType::Cancel;
force_update = true;
break;
- case TouchType::Press:
- case TouchType::Tap:
- case TouchType::Swipe:
- case TouchType::Pinch:
- case TouchType::Rotate:
- type = TouchType::Complete;
+ case GestureType::Press:
+ case GestureType::Tap:
+ case GestureType::Swipe:
+ case GestureType::Pinch:
+ case GestureType::Rotate:
+ type = GestureType::Complete;
force_update = true;
break;
- case TouchType::Pan:
+ case GestureType::Pan:
EndPanEvent(gesture, last_gesture_props, type, time_difference);
break;
default:
@@ -231,15 +211,15 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
}
return;
}
- if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) {
+ if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
gesture.detection_count++;
}
}
void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
- GestureProperties& last_gesture_props, TouchType& type,
- Attribute& attributes) {
- type = TouchType::Tap;
+ GestureProperties& last_gesture_props, GestureType& type,
+ GestureAttribute& attributes) {
+ type = GestureType::Tap;
gesture = last_gesture_props;
force_update = true;
f32 tap_time_difference =
@@ -251,44 +231,42 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
}
void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
- GestureProperties& last_gesture_props, TouchType& type,
+ GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
- auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
const auto& last_entry = GetLastGestureEntry();
- cur_entry.delta = gesture.mid_point - last_entry.pos;
- cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference;
- cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference;
+ next_state.delta = gesture.mid_point - last_entry.pos;
+ next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
+ next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
last_pan_time_difference = time_difference;
// Promote to pinch type
if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
pinch_threshold) {
- type = TouchType::Pinch;
- cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance;
+ type = GestureType::Pinch;
+ next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
}
const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
(1 + (gesture.angle * last_gesture_props.angle)));
// Promote to rotate type
if (std::abs(angle_between_two_lines) > angle_threshold) {
- type = TouchType::Rotate;
- cur_entry.scale = 0;
- cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
+ type = GestureType::Rotate;
+ next_state.scale = 0;
+ next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
}
}
void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
- GestureProperties& last_gesture_props, TouchType& type,
+ GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
- auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
const auto& last_entry = GetLastGestureEntry();
- cur_entry.vel_x =
+ next_state.vel_x =
static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
- cur_entry.vel_y =
+ next_state.vel_y =
static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
const f32 curr_vel =
- std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y));
+ std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
// Set swipe event with parameters
if (curr_vel > swipe_threshold) {
@@ -297,105 +275,50 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
}
// End panning without swipe
- type = TouchType::Complete;
- cur_entry.vel_x = 0;
- cur_entry.vel_y = 0;
+ type = GestureType::Complete;
+ next_state.vel_x = 0;
+ next_state.vel_y = 0;
force_update = true;
}
void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
- GestureProperties& last_gesture_props, TouchType& type) {
- auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+ GestureProperties& last_gesture_props, GestureType& type) {
const auto& last_entry = GetLastGestureEntry();
- type = TouchType::Swipe;
+ type = GestureType::Swipe;
gesture = last_gesture_props;
force_update = true;
- cur_entry.delta = last_entry.delta;
+ next_state.delta = last_entry.delta;
- if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) {
- if (cur_entry.delta.x > 0) {
- cur_entry.direction = Direction::Right;
+ if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
+ if (next_state.delta.x > 0) {
+ next_state.direction = GestureDirection::Right;
return;
}
- cur_entry.direction = Direction::Left;
+ next_state.direction = GestureDirection::Left;
return;
}
- if (cur_entry.delta.y > 0) {
- cur_entry.direction = Direction::Down;
+ if (next_state.delta.y > 0) {
+ next_state.direction = GestureDirection::Down;
return;
}
- cur_entry.direction = Direction::Up;
-}
-
-void Controller_Gesture::OnLoadInputDevices() {
- touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
- touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
- touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
-}
-
-std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
- // Dont assign any touch input to a point if disabled
- if (!Settings::values.touchscreen.enabled) {
- return std::nullopt;
- }
- std::size_t first_free_id = 0;
- while (first_free_id < MAX_POINTS) {
- if (!fingers[first_free_id].pressed) {
- return first_free_id;
- } else {
- first_free_id++;
- }
- }
- return std::nullopt;
-}
-
-Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() {
- return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
+ next_state.direction = GestureDirection::Up;
}
const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
- return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
-}
-
-std::size_t Controller_Gesture::UpdateTouchInputEvent(
- const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
- const auto& [x, y, pressed] = touch_input;
- if (finger_id > MAX_POINTS) {
- LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
- return MAX_POINTS;
- }
- if (pressed) {
- if (finger_id == MAX_POINTS) {
- const auto first_free_id = GetUnusedFingerID();
- if (!first_free_id) {
- // Invalid finger id do nothing
- return MAX_POINTS;
- }
- finger_id = first_free_id.value();
- fingers[finger_id].pressed = true;
- }
- fingers[finger_id].pos = {x, y};
- return finger_id;
- }
-
- if (finger_id != MAX_POINTS) {
- fingers[finger_id].pressed = false;
- }
-
- return MAX_POINTS;
+ return gesture_lifo.ReadCurrentEntry().state;
}
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
GestureProperties gesture;
- std::array<Finger, MAX_POINTS> active_fingers;
+ std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
gesture.active_points =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
for (size_t id = 0; id < gesture.active_points; ++id) {
- const auto& [active_x, active_y] = active_fingers[id].pos;
+ const auto& [active_x, active_y] = active_fingers[id].position;
gesture.points[id] = {
.x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
.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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/point.h"
-#include "core/frontend/input.h"
+#include "core/hid/emulated_console.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class Controller_Gesture final : public ControllerBase {
public:
- explicit Controller_Gesture(Core::System& system_);
+ explicit Controller_Gesture(Core::HID::HIDCore& hid_core_);
~Controller_Gesture() override;
// Called when the controller is initialized
@@ -26,14 +27,12 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
private:
static constexpr size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
- enum class TouchType : u32 {
+ // This is nn::hid::GestureType
+ enum class GestureType : u32 {
Idle, // Nothing touching the screen
Complete, // Set at the end of a touch event
Cancel, // Set when the number of fingers change
@@ -46,7 +45,8 @@ private:
Rotate, // All points rotating from the midpoint
};
- enum class Direction : u32 {
+ // This is nn::hid::GestureDirection
+ enum class GestureDirection : u32 {
None,
Left,
Up,
@@ -54,51 +54,41 @@ private:
Down,
};
- struct Attribute {
+ // This is nn::hid::GestureAttribute
+ struct GestureAttribute {
union {
- u32_le raw{};
+ u32 raw{};
BitField<4, 1, u32> is_new_touch;
BitField<8, 1, u32> is_double_tap;
};
};
- static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size");
+ static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
+ // This is nn::hid::GestureState
struct GestureState {
- s64_le sampling_number;
- s64_le sampling_number2;
- s64_le detection_count;
- TouchType type;
- Direction direction;
- Common::Point<s32_le> pos;
- Common::Point<s32_le> delta;
+ s64 sampling_number;
+ s64 detection_count;
+ GestureType type;
+ GestureDirection direction;
+ Common::Point<s32> pos;
+ Common::Point<s32> delta;
f32 vel_x;
f32 vel_y;
- Attribute attributes;
+ GestureAttribute attributes;
f32 scale;
f32 rotation_angle;
- s32_le point_count;
- std::array<Common::Point<s32_le>, 4> points;
- };
- static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
-
- struct SharedMemory {
- CommonHeader header;
- std::array<GestureState, 17> gesture_states;
- };
- static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
-
- struct Finger {
- Common::Point<f32> pos{};
- bool pressed{};
+ s32 point_count;
+ std::array<Common::Point<s32>, 4> points;
};
+ static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
struct GestureProperties {
- std::array<Common::Point<s32_le>, MAX_POINTS> points{};
+ std::array<Common::Point<s32>, MAX_POINTS> points{};
std::size_t active_points{};
- Common::Point<s32_le> mid_point{};
- s64_le detection_count{};
- u64_le delta_time{};
+ Common::Point<s32> mid_point{};
+ s64 detection_count{};
+ u64 delta_time{};
f32 average_distance{};
f32 angle{};
};
@@ -114,61 +104,48 @@ private:
f32 time_difference);
// Initializes new gesture
- void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes);
+ void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
// Updates existing gesture state
- void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference);
+ void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
// Terminates exiting gesture
void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
- TouchType& type, Attribute& attributes, f32 time_difference);
+ GestureType& type, GestureAttribute& attributes, f32 time_difference);
// Set current event to a tap event
void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
- TouchType& type, Attribute& attributes);
+ GestureType& type, GestureAttribute& attributes);
// Calculates and set the extra parameters related to a pan event
void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
- TouchType& type, f32 time_difference);
+ GestureType& type, f32 time_difference);
// Terminates the pan event
void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
- TouchType& type, f32 time_difference);
+ GestureType& type, f32 time_difference);
// Set current event to a swipe event
void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
- TouchType& type);
-
- // Returns an unused finger id, if there is no fingers available std::nullopt is returned.
- [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const;
+ GestureType& type);
// Retrieves the last gesture entry, as indicated by shared memory indices.
- [[nodiscard]] GestureState& GetLastGestureEntry();
[[nodiscard]] const GestureState& GetLastGestureEntry() const;
- /**
- * If the touch is new it tries to assign a new finger id, if there is no fingers available no
- * changes will be made. Updates the coordinates if the finger id it's already set. If the touch
- * ends delays the output by one frame to set the end_touch flag before finally freeing the
- * finger id
- */
- size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
- size_t finger_id);
-
// Returns the average distance, angle and middle point of the active fingers
GestureProperties GetGestureProperties();
- SharedMemory shared_memory{};
- std::unique_ptr<Input::TouchDevice> touch_mouse_device;
- std::unique_ptr<Input::TouchDevice> touch_udp_device;
- std::unique_ptr<Input::TouchDevice> touch_btn_device;
- std::array<size_t, MAX_FINGERS> mouse_finger_id{};
- std::array<size_t, MAX_FINGERS> keyboard_finger_id{};
- std::array<size_t, MAX_FINGERS> udp_finger_id{};
- std::array<Finger, MAX_POINTS> fingers{};
+ // This is nn::hid::detail::GestureLifo
+ Lifo<GestureState, hid_entry_count> gesture_lifo{};
+ static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
+ GestureState next_state{};
+
+ Core::HID::EmulatedConsole* console;
+
+ std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
GestureProperties last_gesture{};
- s64_le last_update_timestamp{};
- s64_le last_tap_timestamp{};
+ s64 last_update_timestamp{};
+ s64 last_tap_timestamp{};
f32 last_pan_time_difference{};
bool force_update{false};
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 @@
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
+#include "core/hid/emulated_devices.h"
+#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/keyboard.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
-constexpr u8 KEYS_PER_BYTE = 8;
-Controller_Keyboard::Controller_Keyboard(Core::System& system_) : ControllerBase{system_} {}
+Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_)
+ : ControllerBase{hid_core_} {
+ emulated_devices = hid_core.GetEmulatedDevices();
+}
+
Controller_Keyboard::~Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}
@@ -21,51 +26,27 @@ void Controller_Keyboard::OnRelease() {}
void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetCPUTicks();
- shared_memory.header.total_entry_count = 17;
-
if (!IsControllerActivated()) {
- shared_memory.header.entry_count = 0;
- shared_memory.header.last_entry_index = 0;
+ keyboard_lifo.buffer_count = 0;
+ keyboard_lifo.buffer_tail = 0;
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
return;
}
- shared_memory.header.entry_count = 16;
-
- const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
- shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
- auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
- cur_entry.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number2 = cur_entry.sampling_number;
+ const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state;
+ next_state.sampling_number = last_entry.sampling_number + 1;
- cur_entry.key.fill(0);
if (Settings::values.keyboard_enabled) {
- for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
- auto& entry = cur_entry.key[i / KEYS_PER_BYTE];
- entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)));
- }
+ const auto& keyboard_state = emulated_devices->GetKeyboard();
+ const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
- using namespace Settings::NativeKeyboard;
-
- // TODO: Assign the correct key to all modifiers
- cur_entry.modifier.control.Assign(keyboard_mods[LeftControl]->GetStatus());
- cur_entry.modifier.shift.Assign(keyboard_mods[LeftShift]->GetStatus());
- cur_entry.modifier.left_alt.Assign(keyboard_mods[LeftAlt]->GetStatus());
- cur_entry.modifier.right_alt.Assign(keyboard_mods[RightAlt]->GetStatus());
- cur_entry.modifier.gui.Assign(0);
- cur_entry.modifier.caps_lock.Assign(keyboard_mods[CapsLock]->GetStatus());
- cur_entry.modifier.scroll_lock.Assign(keyboard_mods[ScrollLock]->GetStatus());
- cur_entry.modifier.num_lock.Assign(keyboard_mods[NumLock]->GetStatus());
- cur_entry.modifier.katakana.Assign(0);
- cur_entry.modifier.hiragana.Assign(0);
+ next_state.key = keyboard_state;
+ next_state.modifier = keyboard_modifier_state;
+ next_state.attribute.is_connected.Assign(1);
}
- std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
-}
-void Controller_Keyboard::OnLoadInputDevices() {
- std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
- keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
- std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
- keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
+ keyboard_lifo.WriteNextEntry(next_state);
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
}
+
} // 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 @@
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/settings.h"
#include "common/swap.h"
-#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/ring_lifo.h"
+
+namespace Core::HID {
+class EmulatedDevices;
+struct KeyboardModifier;
+struct KeyboardKey;
+} // namespace Core::HID
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
public:
- explicit Controller_Keyboard(Core::System& system_);
+ explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_);
~Controller_Keyboard() override;
// Called when the controller is initialized
@@ -28,47 +33,21 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
private:
- struct Modifiers {
- union {
- u32_le raw{};
- BitField<0, 1, u32> control;
- BitField<1, 1, u32> shift;
- BitField<2, 1, u32> left_alt;
- BitField<3, 1, u32> right_alt;
- BitField<4, 1, u32> gui;
- BitField<8, 1, u32> caps_lock;
- BitField<9, 1, u32> scroll_lock;
- BitField<10, 1, u32> num_lock;
- BitField<11, 1, u32> katakana;
- BitField<12, 1, u32> hiragana;
- };
- };
- static_assert(sizeof(Modifiers) == 0x4, "Modifiers is an invalid size");
-
+ // This is nn::hid::detail::KeyboardState
struct KeyboardState {
- s64_le sampling_number;
- s64_le sampling_number2;
-
- Modifiers modifier;
- std::array<u8, 32> key;
+ s64 sampling_number;
+ Core::HID::KeyboardModifier modifier;
+ Core::HID::KeyboardAttribute attribute;
+ Core::HID::KeyboardKey key;
};
- static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
+ static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
- struct SharedMemory {
- CommonHeader header;
- std::array<KeyboardState, 17> pad_states;
- INSERT_PADDING_BYTES(0x28);
- };
- static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
- SharedMemory shared_memory{};
+ // This is nn::hid::detail::KeyboardLifo
+ Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
+ static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
+ KeyboardState next_state{};
- std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
- keyboard_keys;
- std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
- keyboard_mods;
+ Core::HID::EmulatedDevices* emulated_devices;
};
} // 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 @@
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
+#include "core/hid/emulated_devices.h"
+#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/mouse.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
-Controller_Mouse::Controller_Mouse(Core::System& system_) : ControllerBase{system_} {}
+Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
+ emulated_devices = hid_core.GetEmulatedDevices();
+}
+
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
@@ -19,50 +24,35 @@ void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetCPUTicks();
- shared_memory.header.total_entry_count = 17;
-
if (!IsControllerActivated()) {
- shared_memory.header.entry_count = 0;
- shared_memory.header.last_entry_index = 0;
+ mouse_lifo.buffer_count = 0;
+ mouse_lifo.buffer_tail = 0;
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
return;
}
- shared_memory.header.entry_count = 16;
- auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
- shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
- auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
+ const auto& last_entry = mouse_lifo.ReadCurrentEntry().state;
+ next_state.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number2 = cur_entry.sampling_number;
-
- cur_entry.attribute.raw = 0;
+ next_state.attribute.raw = 0;
if (Settings::values.mouse_enabled) {
- const auto [px, py, sx, sy] = mouse_device->GetStatus();
- const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
- const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
- cur_entry.x = x;
- cur_entry.y = y;
- cur_entry.delta_x = x - last_entry.x;
- cur_entry.delta_y = y - last_entry.y;
- cur_entry.mouse_wheel_x = sx;
- cur_entry.mouse_wheel_y = sy;
- cur_entry.attribute.is_connected.Assign(1);
-
- using namespace Settings::NativeMouseButton;
- cur_entry.button.left.Assign(mouse_button_devices[Left]->GetStatus());
- cur_entry.button.right.Assign(mouse_button_devices[Right]->GetStatus());
- cur_entry.button.middle.Assign(mouse_button_devices[Middle]->GetStatus());
- cur_entry.button.forward.Assign(mouse_button_devices[Forward]->GetStatus());
- cur_entry.button.back.Assign(mouse_button_devices[Back]->GetStatus());
+ const auto& mouse_button_state = emulated_devices->GetMouseButtons();
+ const auto& mouse_position_state = emulated_devices->GetMousePosition();
+ const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
+ next_state.attribute.is_connected.Assign(1);
+ next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
+ next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
+ next_state.delta_x = next_state.x - last_entry.x;
+ next_state.delta_y = next_state.y - last_entry.y;
+ next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
+ next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
+
+ last_mouse_wheel_state = mouse_wheel_state;
+ next_state.button = mouse_button_state;
}
- std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
+ mouse_lifo.WriteNextEntry(next_state);
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
}
-void Controller_Mouse::OnLoadInputDevices() {
- mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
- std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
- mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
-}
} // 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 @@
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
-#include "common/settings.h"
#include "common/swap.h"
-#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/ring_lifo.h"
+
+namespace Core::HID {
+class EmulatedDevices;
+struct MouseState;
+struct AnalogStickState;
+} // namespace Core::HID
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
public:
- explicit Controller_Mouse(Core::System& system_);
+ explicit Controller_Mouse(Core::HID::HIDCore& hid_core_);
~Controller_Mouse() override;
// Called when the controller is initialized
@@ -27,53 +32,13 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
private:
- struct Buttons {
- union {
- u32_le raw{};
- BitField<0, 1, u32> left;
- BitField<1, 1, u32> right;
- BitField<2, 1, u32> middle;
- BitField<3, 1, u32> forward;
- BitField<4, 1, u32> back;
- };
- };
- static_assert(sizeof(Buttons) == 0x4, "Buttons is an invalid size");
-
- struct Attributes {
- union {
- u32_le raw{};
- BitField<0, 1, u32> transferable;
- BitField<1, 1, u32> is_connected;
- };
- };
- static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
-
- struct MouseState {
- s64_le sampling_number;
- s64_le sampling_number2;
- s32_le x;
- s32_le y;
- s32_le delta_x;
- s32_le delta_y;
- s32_le mouse_wheel_x;
- s32_le mouse_wheel_y;
- Buttons button;
- Attributes attribute;
- };
- static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
-
- struct SharedMemory {
- CommonHeader header;
- std::array<MouseState, 17> mouse_states;
- };
- SharedMemory shared_memory{};
+ // This is nn::hid::detail::MouseLifo
+ Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
+ static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
+ Core::HID::MouseState next_state{};
- std::unique_ptr<Input::MouseDevice> mouse_device;
- std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
- mouse_button_devices;
+ Core::HID::AnalogStickState last_mouse_wheel_state;
+ Core::HID::EmulatedDevices* emulated_devices;
};
} // 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 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
-#include "core/core.h"
#include "core/core_timing.h"
-#include "core/frontend/input.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
@@ -20,120 +20,26 @@
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
-constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
-constexpr s32 HID_TRIGGER_MAX = 0x7fff;
-[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
-constexpr u32 BATTERY_FULL = 2;
-constexpr u32 MAX_NPAD_ID = 7;
-constexpr std::size_t HANDHELD_INDEX = 8;
-constexpr std::array<u32, 10> npad_id_list{
- 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
+constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
+ Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
+ Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
+ Core::HID::NpadIdType::Player7, Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other,
+ Core::HID::NpadIdType::Handheld,
};
-enum class JoystickId : std::size_t {
- Joystick_Left,
- Joystick_Right,
-};
-
-Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
- Settings::ControllerType type) {
- switch (type) {
- case Settings::ControllerType::ProController:
- return NPadControllerType::ProController;
- case Settings::ControllerType::DualJoyconDetached:
- return NPadControllerType::JoyDual;
- case Settings::ControllerType::LeftJoycon:
- return NPadControllerType::JoyLeft;
- case Settings::ControllerType::RightJoycon:
- return NPadControllerType::JoyRight;
- case Settings::ControllerType::Handheld:
- return NPadControllerType::Handheld;
- case Settings::ControllerType::GameCube:
- return NPadControllerType::GameCube;
- default:
- UNREACHABLE();
- return NPadControllerType::ProController;
- }
-}
-
-Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
- Controller_NPad::NPadControllerType type) {
- switch (type) {
- case NPadControllerType::ProController:
- return Settings::ControllerType::ProController;
- case NPadControllerType::JoyDual:
- return Settings::ControllerType::DualJoyconDetached;
- case NPadControllerType::JoyLeft:
- return Settings::ControllerType::LeftJoycon;
- case NPadControllerType::JoyRight:
- return Settings::ControllerType::RightJoycon;
- case NPadControllerType::Handheld:
- return Settings::ControllerType::Handheld;
- case NPadControllerType::GameCube:
- return Settings::ControllerType::GameCube;
- default:
- UNREACHABLE();
- return Settings::ControllerType::ProController;
- }
-}
-
-std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
+bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
switch (npad_id) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- return npad_id;
- case HANDHELD_INDEX:
- case NPAD_HANDHELD:
- return HANDHELD_INDEX;
- case 9:
- case NPAD_UNKNOWN:
- return 9;
- default:
- UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
- return 0;
- }
-}
-
-u32 Controller_NPad::IndexToNPad(std::size_t index) {
- switch (index) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- return static_cast<u32>(index);
- case HANDHELD_INDEX:
- return NPAD_HANDHELD;
- case 9:
- return NPAD_UNKNOWN;
- default:
- UNIMPLEMENTED_MSG("Unknown npad index {}", index);
- return 0;
- }
-}
-
-bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
- switch (npad_id) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case NPAD_UNKNOWN:
- case NPAD_HANDHELD:
+ case Core::HID::NpadIdType::Player1:
+ case Core::HID::NpadIdType::Player2:
+ case Core::HID::NpadIdType::Player3:
+ case Core::HID::NpadIdType::Player4:
+ case Core::HID::NpadIdType::Player5:
+ case Core::HID::NpadIdType::Player6:
+ case Core::HID::NpadIdType::Player7:
+ case Core::HID::NpadIdType::Player8:
+ case Core::HID::NpadIdType::Other:
+ case Core::HID::NpadIdType::Handheld:
return true;
default:
LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
@@ -141,305 +47,337 @@ bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
}
}
-bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) {
- return IsNpadIdValid(device_handle.npad_id) &&
- device_handle.npad_type < NpadType::MaxNpadType &&
- device_handle.device_index < DeviceIndex::MaxDeviceIndex;
+bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
+ return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
+ device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
+ device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
}
-Controller_NPad::Controller_NPad(Core::System& system_,
+bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
+ return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
+ device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
+ device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+}
+
+Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
KernelHelpers::ServiceContext& service_context_)
- : ControllerBase{system_}, service_context{service_context_} {
- latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE});
+ : ControllerBase{hid_core_}, service_context{service_context_} {
+ for (std::size_t i = 0; i < controller_data.size(); ++i) {
+ auto& controller = controller_data[i];
+ controller.device = hid_core.GetEmulatedControllerByIndex(i);
+ controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
+ DEFAULT_VIBRATION_VALUE;
+ controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
+ DEFAULT_VIBRATION_VALUE;
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this,
+ i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
+ .is_npad_service = true,
+ };
+ controller.callback_key = controller.device->SetCallback(engine_callback);
+ }
}
Controller_NPad::~Controller_NPad() {
+ for (std::size_t i = 0; i < controller_data.size(); ++i) {
+ auto& controller = controller_data[i];
+ controller.device->DeleteCallback(controller.callback_key);
+ }
OnRelease();
}
-void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
- const auto controller_type = connected_controllers[controller_idx].type;
- auto& controller = shared_memory_entries[controller_idx];
- if (controller_type == NPadControllerType::None) {
- styleset_changed_events[controller_idx]->GetWritableEvent().Signal();
+void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
+ std::size_t controller_idx) {
+ if (type == Core::HID::ControllerTriggerType::All) {
+ ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
+ ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
+ return;
+ }
+ if (controller_idx >= controller_data.size()) {
+ return;
+ }
+
+ auto& controller = controller_data[controller_idx];
+ const auto is_connected = controller.device->IsConnected();
+ const auto npad_type = controller.device->GetNpadStyleIndex();
+ const auto npad_id = controller.device->GetNpadIdType();
+ switch (type) {
+ case Core::HID::ControllerTriggerType::Connected:
+ case Core::HID::ControllerTriggerType::Disconnected:
+ if (is_connected == controller.is_connected) {
+ return;
+ }
+ UpdateControllerAt(npad_type, npad_id, is_connected);
+ break;
+ case Core::HID::ControllerTriggerType::Battery: {
+ if (!controller.device->IsConnected()) {
+ return;
+ }
+ auto& shared_memory = controller.shared_memory_entry;
+ const auto& battery_level = controller.device->GetBattery();
+ shared_memory.battery_level_dual = battery_level.dual.battery_level;
+ shared_memory.battery_level_left = battery_level.left.battery_level;
+ shared_memory.battery_level_right = battery_level.right.battery_level;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
+ auto& controller = GetControllerFromNpadIdType(npad_id);
+ if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
return;
}
- controller.style_set.raw = 0; // Zero out
- controller.device_type.raw = 0;
- controller.system_properties.raw = 0;
+ LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
+ const auto controller_type = controller.device->GetNpadStyleIndex();
+ auto& shared_memory = controller.shared_memory_entry;
+ if (controller_type == Core::HID::NpadStyleIndex::None) {
+ controller.styleset_changed_event->GetWritableEvent().Signal();
+ return;
+ }
+ shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None;
+ shared_memory.device_type.raw = 0;
+ shared_memory.system_properties.raw = 0;
switch (controller_type) {
- case NPadControllerType::None:
+ case Core::HID::NpadStyleIndex::None:
UNREACHABLE();
break;
- case NPadControllerType::ProController:
- controller.style_set.fullkey.Assign(1);
- controller.device_type.fullkey.Assign(1);
- controller.system_properties.is_vertical.Assign(1);
- controller.system_properties.use_plus.Assign(1);
- controller.system_properties.use_minus.Assign(1);
- controller.assignment_mode = NpadAssignments::Single;
- controller.footer_type = AppletFooterUiType::SwitchProController;
+ case Core::HID::NpadStyleIndex::ProController:
+ shared_memory.style_tag.fullkey.Assign(1);
+ shared_memory.device_type.fullkey.Assign(1);
+ shared_memory.system_properties.is_vertical.Assign(1);
+ shared_memory.system_properties.use_plus.Assign(1);
+ shared_memory.system_properties.use_minus.Assign(1);
+ shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
break;
- case NPadControllerType::Handheld:
- controller.style_set.handheld.Assign(1);
- controller.device_type.handheld_left.Assign(1);
- controller.device_type.handheld_right.Assign(1);
- controller.system_properties.is_vertical.Assign(1);
- controller.system_properties.use_plus.Assign(1);
- controller.system_properties.use_minus.Assign(1);
- controller.assignment_mode = NpadAssignments::Dual;
- controller.footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
+ case Core::HID::NpadStyleIndex::Handheld:
+ shared_memory.style_tag.handheld.Assign(1);
+ shared_memory.device_type.handheld_left.Assign(1);
+ shared_memory.device_type.handheld_right.Assign(1);
+ shared_memory.system_properties.is_vertical.Assign(1);
+ shared_memory.system_properties.use_plus.Assign(1);
+ shared_memory.system_properties.use_minus.Assign(1);
+ shared_memory.system_properties.use_directional_buttons.Assign(1);
+ shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
+ shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
+ break;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ shared_memory.style_tag.joycon_dual.Assign(1);
+ if (controller.is_dual_left_connected) {
+ shared_memory.device_type.joycon_left.Assign(1);
+ shared_memory.system_properties.use_minus.Assign(1);
+ }
+ if (controller.is_dual_right_connected) {
+ shared_memory.device_type.joycon_right.Assign(1);
+ shared_memory.system_properties.use_plus.Assign(1);
+ }
+ shared_memory.system_properties.use_directional_buttons.Assign(1);
+ shared_memory.system_properties.is_vertical.Assign(1);
+ shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
+ if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
+ } else if (controller.is_dual_left_connected) {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
+ } else {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
+ }
break;
- case NPadControllerType::JoyDual:
- controller.style_set.joycon_dual.Assign(1);
- controller.device_type.joycon_left.Assign(1);
- controller.device_type.joycon_right.Assign(1);
- controller.system_properties.is_vertical.Assign(1);
- controller.system_properties.use_plus.Assign(1);
- controller.system_properties.use_minus.Assign(1);
- controller.assignment_mode = NpadAssignments::Dual;
- controller.footer_type = AppletFooterUiType::JoyDual;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ shared_memory.style_tag.joycon_left.Assign(1);
+ shared_memory.device_type.joycon_left.Assign(1);
+ shared_memory.system_properties.is_horizontal.Assign(1);
+ shared_memory.system_properties.use_minus.Assign(1);
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
break;
- case NPadControllerType::JoyLeft:
- controller.style_set.joycon_left.Assign(1);
- controller.device_type.joycon_left.Assign(1);
- controller.system_properties.is_horizontal.Assign(1);
- controller.system_properties.use_minus.Assign(1);
- controller.assignment_mode = NpadAssignments::Single;
- controller.footer_type = AppletFooterUiType::JoyLeftHorizontal;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ shared_memory.style_tag.joycon_right.Assign(1);
+ shared_memory.device_type.joycon_right.Assign(1);
+ shared_memory.system_properties.is_horizontal.Assign(1);
+ shared_memory.system_properties.use_plus.Assign(1);
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
break;
- case NPadControllerType::JoyRight:
- controller.style_set.joycon_right.Assign(1);
- controller.device_type.joycon_right.Assign(1);
- controller.system_properties.is_horizontal.Assign(1);
- controller.system_properties.use_plus.Assign(1);
- controller.assignment_mode = NpadAssignments::Single;
- controller.footer_type = AppletFooterUiType::JoyRightHorizontal;
+ case Core::HID::NpadStyleIndex::GameCube:
+ shared_memory.style_tag.gamecube.Assign(1);
+ shared_memory.device_type.fullkey.Assign(1);
+ shared_memory.system_properties.is_vertical.Assign(1);
+ shared_memory.system_properties.use_plus.Assign(1);
break;
- case NPadControllerType::GameCube:
- controller.style_set.gamecube.Assign(1);
- // The GC Controller behaves like a wired Pro Controller
- controller.device_type.fullkey.Assign(1);
- controller.system_properties.is_vertical.Assign(1);
- controller.system_properties.use_plus.Assign(1);
+ case Core::HID::NpadStyleIndex::Pokeball:
+ shared_memory.style_tag.palma.Assign(1);
+ shared_memory.device_type.palma.Assign(1);
break;
- case NPadControllerType::Pokeball:
- controller.style_set.palma.Assign(1);
- controller.device_type.palma.Assign(1);
- controller.assignment_mode = NpadAssignments::Single;
+ case Core::HID::NpadStyleIndex::NES:
+ shared_memory.style_tag.lark.Assign(1);
+ shared_memory.device_type.fullkey.Assign(1);
+ break;
+ case Core::HID::NpadStyleIndex::SNES:
+ shared_memory.style_tag.lucia.Assign(1);
+ shared_memory.device_type.fullkey.Assign(1);
+ shared_memory.applet_footer.type = AppletFooterUiType::Lucia;
+ break;
+ case Core::HID::NpadStyleIndex::N64:
+ shared_memory.style_tag.lagoon.Assign(1);
+ shared_memory.device_type.fullkey.Assign(1);
+ shared_memory.applet_footer.type = AppletFooterUiType::Lagon;
+ break;
+ case Core::HID::NpadStyleIndex::SegaGenesis:
+ shared_memory.style_tag.lager.Assign(1);
+ shared_memory.device_type.fullkey.Assign(1);
+ break;
+ default:
break;
}
- controller.fullkey_color.attribute = ColorAttributes::Ok;
- controller.fullkey_color.fullkey.body = 0;
- controller.fullkey_color.fullkey.button = 0;
+ const auto& body_colors = controller.device->GetColors();
- controller.joycon_color.attribute = ColorAttributes::Ok;
- controller.joycon_color.left.body =
- Settings::values.players.GetValue()[controller_idx].body_color_left;
- controller.joycon_color.left.button =
- Settings::values.players.GetValue()[controller_idx].button_color_left;
- controller.joycon_color.right.body =
- Settings::values.players.GetValue()[controller_idx].body_color_right;
- controller.joycon_color.right.button =
- Settings::values.players.GetValue()[controller_idx].button_color_right;
+ shared_memory.fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory.fullkey_color.fullkey = body_colors.fullkey;
- // TODO: Investigate when we should report all batery types
- controller.battery_level_dual = BATTERY_FULL;
- controller.battery_level_left = BATTERY_FULL;
- controller.battery_level_right = BATTERY_FULL;
+ shared_memory.joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory.joycon_color.left = body_colors.left;
+ shared_memory.joycon_color.right = body_colors.right;
- SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
+ // TODO: Investigate when we should report all batery types
+ const auto& battery_level = controller.device->GetBattery();
+ shared_memory.battery_level_dual = battery_level.dual.battery_level;
+ shared_memory.battery_level_left = battery_level.left.battery_level;
+ shared_memory.battery_level_right = battery_level.right.battery_level;
+
+ controller.is_connected = true;
+ controller.device->Connect();
+ SignalStyleSetChangedEvent(npad_id);
+ WriteEmptyEntry(controller.shared_memory_entry);
}
void Controller_NPad::OnInit() {
- for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
- styleset_changed_events[i] =
- service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
- }
-
if (!IsControllerActivated()) {
return;
}
- OnLoadInputDevices();
-
- if (style.raw == 0) {
- // We want to support all controllers
- style.handheld.Assign(1);
- style.joycon_left.Assign(1);
- style.joycon_right.Assign(1);
- style.joycon_dual.Assign(1);
- style.fullkey.Assign(1);
- style.gamecube.Assign(1);
- style.palma.Assign(1);
- }
-
- std::transform(Settings::values.players.GetValue().begin(),
- Settings::values.players.GetValue().end(), connected_controllers.begin(),
- [](const Settings::PlayerInput& player) {
- return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
- player.connected};
- });
-
- // Connect the Player 1 or Handheld controller if none are connected.
- if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
- [](const ControllerHolder& controller) { return controller.is_connected; })) {
- const auto controller =
- MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
- if (controller == NPadControllerType::Handheld) {
- Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
- connected_controllers[HANDHELD_INDEX] = {controller, true};
- } else {
- Settings::values.players.GetValue()[0].connected = true;
- connected_controllers[0] = {controller, true};
- }
+ for (std::size_t i = 0; i < controller_data.size(); ++i) {
+ auto& controller = controller_data[i];
+ controller.styleset_changed_event =
+ service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
}
- // Account for handheld
- if (connected_controllers[HANDHELD_INDEX].is_connected) {
- connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
+ if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
+ // We want to support all controllers
+ hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
}
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
- npad_id_list.size() * sizeof(u32));
+ npad_id_list.size() * sizeof(Core::HID::NpadIdType));
+
+ // Prefill controller buffers
+ for (auto& controller : controller_data) {
+ auto& npad = controller.shared_memory_entry;
+ npad.fullkey_color = {
+ .attribute = ColorAttribute::NoController,
+ .fullkey = {},
+ };
+ npad.joycon_color = {
+ .attribute = ColorAttribute::NoController,
+ .left = {},
+ .right = {},
+ };
+ // HW seems to initialize the first 19 entries
+ for (std::size_t i = 0; i < 19; ++i) {
+ WriteEmptyEntry(npad);
+ }
+ }
- for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
- const auto& controller = connected_controllers[i];
- if (controller.is_connected) {
- AddNewControllerAt(controller.type, i);
+ // Connect controllers
+ for (auto& controller : controller_data) {
+ const auto& device = controller.device;
+ if (device->IsConnected()) {
+ AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
}
}
}
-void Controller_NPad::OnLoadInputDevices() {
- const auto& players = Settings::values.players.GetValue();
+void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
+ NPadGenericState dummy_pad_state{};
+ NpadGcTriggerState dummy_gc_state{};
+ dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad.fullkey_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad.handheld_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad.joy_left_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad.joy_right_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad.palma_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad.system_ext_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
+}
- std::lock_guard lock{mutex};
- for (std::size_t i = 0; i < players.size(); ++i) {
- std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
- players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
- buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
- std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
- players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
- sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
- std::transform(players[i].vibrations.begin() +
- Settings::NativeVibration::VIBRATION_HID_BEGIN,
- players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
- vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
- std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
- players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
- motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
- for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
- InitializeVibrationDeviceAtIndex(i, device_idx);
+void Controller_NPad::OnRelease() {
+ for (std::size_t i = 0; i < controller_data.size(); ++i) {
+ auto& controller = controller_data[i];
+ service_context.CloseEvent(controller.styleset_changed_event);
+ for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
+ VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {});
}
}
}
-void Controller_NPad::OnRelease() {
- for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
- for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
- VibrateControllerAtIndex(npad_idx, device_idx, {});
- }
+void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
+ std::lock_guard lock{mutex};
+ auto& controller = GetControllerFromNpadIdType(npad_id);
+ const auto controller_type = controller.device->GetNpadStyleIndex();
+ if (!controller.device->IsConnected()) {
+ return;
}
- for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
- service_context.CloseEvent(styleset_changed_events[i]);
+ auto& pad_entry = controller.npad_pad_state;
+ auto& trigger_entry = controller.npad_trigger_state;
+ const auto button_state = controller.device->GetNpadButtons();
+ const auto stick_state = controller.device->GetSticks();
+
+ using btn = Core::HID::NpadButton;
+ pad_entry.npad_buttons.raw = btn::None;
+ if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) {
+ constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R |
+ btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp |
+ btn::StickRRight | btn::StickRDown;
+ pad_entry.npad_buttons.raw = button_state.raw & right_button_mask;
+ pad_entry.r_stick = stick_state.right;
}
-}
-void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
- std::lock_guard lock{mutex};
+ if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) {
+ constexpr btn left_button_mask =
+ btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL |
+ btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown;
+ pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask;
+ pad_entry.l_stick = stick_state.left;
+ }
- const auto controller_idx = NPadIdToIndex(npad_id);
- const auto controller_type = connected_controllers[controller_idx].type;
- if (!connected_controllers[controller_idx].is_connected) {
- return;
+ if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) {
+ pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
+ pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
}
- auto& pad_state = npad_pad_states[controller_idx].pad_states;
- auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
- auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
- auto& trigger_entry = npad_trigger_states[controller_idx];
- const auto& button_state = buttons[controller_idx];
- const auto& analog_state = sticks[controller_idx];
- const auto [stick_l_x_f, stick_l_y_f] =
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
- const auto [stick_r_x_f, stick_r_y_f] =
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
-
- using namespace Settings::NativeButton;
- if (controller_type != NPadControllerType::JoyLeft) {
- pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.r_stick_right.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
- pad_state.r_stick_left.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
- pad_state.r_stick_up.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
- pad_state.r_stick_down.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
- rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
- rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
- }
-
- if (controller_type != NPadControllerType::JoyRight) {
- pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.l_stick_right.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
- pad_state.l_stick_left.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
- pad_state.l_stick_up.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
- pad_state.l_stick_down.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
- lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
- lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
- }
-
- if (controller_type == NPadControllerType::JoyLeft) {
- pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
- }
-
- if (controller_type == NPadControllerType::JoyRight) {
- pad_state.right_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.right_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
- }
-
- if (controller_type == NPadControllerType::GameCube) {
- trigger_entry.l_analog = static_cast<s32>(
- button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
- trigger_entry.r_analog = static_cast<s32>(
- button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
- pad_state.zl.Assign(false);
- pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
+
+ if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) {
+ pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
+ pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
+ }
+
+ if (controller_type == Core::HID::NpadStyleIndex::GameCube) {
+ const auto& trigger_state = controller.device->GetTriggers();
+ trigger_entry.l_analog = trigger_state.left;
+ trigger_entry.r_analog = trigger_state.right;
+ pad_entry.npad_buttons.zl.Assign(false);
+ pad_entry.npad_buttons.zr.Assign(button_state.r);
+ pad_entry.npad_buttons.l.Assign(button_state.zl);
+ pad_entry.npad_buttons.r.Assign(button_state.zr);
}
}
@@ -448,173 +386,136 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
if (!IsControllerActivated()) {
return;
}
- for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
- auto& npad = shared_memory_entries[i];
- const std::array<NPadGeneric*, 7> controller_npads{
- &npad.fullkey_states, &npad.handheld_states, &npad.joy_dual_states,
- &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states,
- &npad.system_ext_states};
-
- // There is the posibility to have more controllers with analog triggers
- const std::array<TriggerGeneric*, 1> controller_triggers{
- &npad.gc_trigger_states,
- };
-
- for (auto* main_controller : controller_npads) {
- main_controller->common.entry_count = 16;
- main_controller->common.total_entry_count = 17;
-
- const auto& last_entry =
- main_controller->npad[main_controller->common.last_entry_index];
-
- main_controller->common.timestamp = core_timing.GetCPUTicks();
- main_controller->common.last_entry_index =
- (main_controller->common.last_entry_index + 1) % 17;
-
- auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
-
- cur_entry.timestamp = last_entry.timestamp + 1;
- cur_entry.timestamp2 = cur_entry.timestamp;
- }
-
- for (auto* analog_trigger : controller_triggers) {
- analog_trigger->entry_count = 16;
- analog_trigger->total_entry_count = 17;
-
- const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
-
- analog_trigger->timestamp = core_timing.GetCPUTicks();
- analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
-
- auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
- cur_entry.timestamp = last_entry.timestamp + 1;
- cur_entry.timestamp2 = cur_entry.timestamp;
- }
+ for (std::size_t i = 0; i < controller_data.size(); ++i) {
+ auto& controller = controller_data[i];
+ auto& npad = controller.shared_memory_entry;
- const auto& controller_type = connected_controllers[i].type;
+ const auto& controller_type = controller.device->GetNpadStyleIndex();
- if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
+ if (controller_type == Core::HID::NpadStyleIndex::None ||
+ !controller.device->IsConnected()) {
+ // Refresh shared memory
+ std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
+ &controller.shared_memory_entry, sizeof(NpadInternalState));
continue;
}
- const u32 npad_index = static_cast<u32>(i);
-
- RequestPadStateUpdate(npad_index);
- auto& pad_state = npad_pad_states[npad_index];
- auto& trigger_state = npad_trigger_states[npad_index];
-
- auto& main_controller =
- npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index];
- auto& handheld_entry =
- npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
- auto& dual_entry = npad.joy_dual_states.npad[npad.joy_dual_states.common.last_entry_index];
- auto& left_entry = npad.joy_left_states.npad[npad.joy_left_states.common.last_entry_index];
- auto& right_entry =
- npad.joy_right_states.npad[npad.joy_right_states.common.last_entry_index];
- auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];
- auto& libnx_entry =
- npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index];
- auto& trigger_entry =
- npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];
-
- libnx_entry.connection_status.raw = 0;
- libnx_entry.connection_status.is_connected.Assign(1);
+ RequestPadStateUpdate(controller.device->GetNpadIdType());
+ auto& pad_state = controller.npad_pad_state;
+ auto& libnx_state = controller.npad_libnx_state;
+ auto& trigger_state = controller.npad_trigger_state;
+
+ // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
+ // any controllers.
+ libnx_state.connection_status.raw = 0;
+ libnx_state.connection_status.is_connected.Assign(1);
switch (controller_type) {
- case NPadControllerType::None:
+ case Core::HID::NpadStyleIndex::None:
UNREACHABLE();
break;
- case NPadControllerType::ProController:
- main_controller.connection_status.raw = 0;
- main_controller.connection_status.is_connected.Assign(1);
- main_controller.connection_status.is_wired.Assign(1);
- main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
- main_controller.pad.l_stick = pad_state.l_stick;
- main_controller.pad.r_stick = pad_state.r_stick;
-
- libnx_entry.connection_status.is_wired.Assign(1);
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::NES:
+ case Core::HID::NpadStyleIndex::SNES:
+ case Core::HID::NpadStyleIndex::N64:
+ case Core::HID::NpadStyleIndex::SegaGenesis:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_wired.Assign(1);
+
+ libnx_state.connection_status.is_wired.Assign(1);
+ pad_state.sampling_number =
+ npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad.fullkey_lifo.WriteNextEntry(pad_state);
break;
- case NPadControllerType::Handheld:
- handheld_entry.connection_status.raw = 0;
- handheld_entry.connection_status.is_connected.Assign(1);
- handheld_entry.connection_status.is_wired.Assign(1);
- handheld_entry.connection_status.is_left_connected.Assign(1);
- handheld_entry.connection_status.is_right_connected.Assign(1);
- handheld_entry.connection_status.is_left_wired.Assign(1);
- handheld_entry.connection_status.is_right_wired.Assign(1);
- handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
- handheld_entry.pad.l_stick = pad_state.l_stick;
- handheld_entry.pad.r_stick = pad_state.r_stick;
-
- libnx_entry.connection_status.is_wired.Assign(1);
- libnx_entry.connection_status.is_left_connected.Assign(1);
- libnx_entry.connection_status.is_right_connected.Assign(1);
- libnx_entry.connection_status.is_left_wired.Assign(1);
- libnx_entry.connection_status.is_right_wired.Assign(1);
+ case Core::HID::NpadStyleIndex::Handheld:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_wired.Assign(1);
+ pad_state.connection_status.is_left_connected.Assign(1);
+ pad_state.connection_status.is_right_connected.Assign(1);
+ pad_state.connection_status.is_left_wired.Assign(1);
+ pad_state.connection_status.is_right_wired.Assign(1);
+
+ libnx_state.connection_status.is_wired.Assign(1);
+ libnx_state.connection_status.is_left_connected.Assign(1);
+ libnx_state.connection_status.is_right_connected.Assign(1);
+ libnx_state.connection_status.is_left_wired.Assign(1);
+ libnx_state.connection_status.is_right_wired.Assign(1);
+ pad_state.sampling_number =
+ npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad.handheld_lifo.WriteNextEntry(pad_state);
break;
- case NPadControllerType::JoyDual:
- dual_entry.connection_status.raw = 0;
- dual_entry.connection_status.is_connected.Assign(1);
- dual_entry.connection_status.is_left_connected.Assign(1);
- dual_entry.connection_status.is_right_connected.Assign(1);
- dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
- dual_entry.pad.l_stick = pad_state.l_stick;
- dual_entry.pad.r_stick = pad_state.r_stick;
-
- libnx_entry.connection_status.is_left_connected.Assign(1);
- libnx_entry.connection_status.is_right_connected.Assign(1);
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ if (controller.is_dual_left_connected) {
+ pad_state.connection_status.is_left_connected.Assign(1);
+ libnx_state.connection_status.is_left_connected.Assign(1);
+ }
+ if (controller.is_dual_right_connected) {
+ pad_state.connection_status.is_right_connected.Assign(1);
+ libnx_state.connection_status.is_right_connected.Assign(1);
+ }
+
+ pad_state.sampling_number =
+ npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad.joy_dual_lifo.WriteNextEntry(pad_state);
break;
- case NPadControllerType::JoyLeft:
- left_entry.connection_status.raw = 0;
- left_entry.connection_status.is_connected.Assign(1);
- left_entry.connection_status.is_left_connected.Assign(1);
- left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
- left_entry.pad.l_stick = pad_state.l_stick;
- left_entry.pad.r_stick = pad_state.r_stick;
-
- libnx_entry.connection_status.is_left_connected.Assign(1);
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_left_connected.Assign(1);
+
+ libnx_state.connection_status.is_left_connected.Assign(1);
+ pad_state.sampling_number =
+ npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad.joy_left_lifo.WriteNextEntry(pad_state);
break;
- case NPadControllerType::JoyRight:
- right_entry.connection_status.raw = 0;
- right_entry.connection_status.is_connected.Assign(1);
- right_entry.connection_status.is_right_connected.Assign(1);
- right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
- right_entry.pad.l_stick = pad_state.l_stick;
- right_entry.pad.r_stick = pad_state.r_stick;
-
- libnx_entry.connection_status.is_right_connected.Assign(1);
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_right_connected.Assign(1);
+
+ libnx_state.connection_status.is_right_connected.Assign(1);
+ pad_state.sampling_number =
+ npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad.joy_right_lifo.WriteNextEntry(pad_state);
break;
- case NPadControllerType::GameCube:
- main_controller.connection_status.raw = 0;
- main_controller.connection_status.is_connected.Assign(1);
- main_controller.connection_status.is_wired.Assign(1);
- main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
- main_controller.pad.l_stick = pad_state.l_stick;
- main_controller.pad.r_stick = pad_state.r_stick;
- trigger_entry.l_analog = trigger_state.l_analog;
- trigger_entry.r_analog = trigger_state.r_analog;
-
- libnx_entry.connection_status.is_wired.Assign(1);
+ case Core::HID::NpadStyleIndex::GameCube:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_wired.Assign(1);
+
+ libnx_state.connection_status.is_wired.Assign(1);
+ pad_state.sampling_number =
+ npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ trigger_state.sampling_number =
+ npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad.fullkey_lifo.WriteNextEntry(pad_state);
+ npad.gc_trigger_lifo.WriteNextEntry(trigger_state);
break;
- case NPadControllerType::Pokeball:
- pokeball_entry.connection_status.raw = 0;
- pokeball_entry.connection_status.is_connected.Assign(1);
- pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
- pokeball_entry.pad.l_stick = pad_state.l_stick;
- pokeball_entry.pad.r_stick = pad_state.r_stick;
+ case Core::HID::NpadStyleIndex::Pokeball:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.sampling_number =
+ npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad.palma_lifo.WriteNextEntry(pad_state);
+ break;
+ default:
break;
}
- // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
- // any controllers.
- libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
- libnx_entry.pad.l_stick = pad_state.l_stick;
- libnx_entry.pad.r_stick = pad_state.r_stick;
+ libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
+ libnx_state.l_stick = pad_state.l_stick;
+ libnx_state.r_stick = pad_state.r_stick;
+ npad.system_ext_lifo.WriteNextEntry(pad_state);
+
+ press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
- press_state |= static_cast<u32>(pad_state.pad_states.raw);
+ std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
+ &controller.shared_memory_entry, sizeof(NpadInternalState));
}
- std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
- shared_memory_entries.size() * sizeof(NPadEntry));
}
void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
@@ -622,145 +523,138 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
if (!IsControllerActivated()) {
return;
}
- for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
- auto& npad = shared_memory_entries[i];
-
- const auto& controller_type = connected_controllers[i].type;
- if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
- continue;
- }
-
- const std::array<SixAxisGeneric*, 6> controller_sixaxes{
- &npad.sixaxis_fullkey, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
- &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
- };
-
- for (auto* sixaxis_sensor : controller_sixaxes) {
- sixaxis_sensor->common.entry_count = 16;
- sixaxis_sensor->common.total_entry_count = 17;
-
- const auto& last_entry =
- sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
-
- sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
- sixaxis_sensor->common.last_entry_index =
- (sixaxis_sensor->common.last_entry_index + 1) % 17;
+ for (std::size_t i = 0; i < controller_data.size(); ++i) {
+ auto& controller = controller_data[i];
- auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+ const auto& controller_type = controller.device->GetNpadStyleIndex();
- cur_entry.timestamp = last_entry.timestamp + 1;
- cur_entry.timestamp2 = cur_entry.timestamp;
+ if (controller_type == Core::HID::NpadStyleIndex::None ||
+ !controller.device->IsConnected()) {
+ continue;
}
- // Try to read sixaxis sensor states
- std::array<MotionDevice, 2> motion_devices;
-
- if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) {
- sixaxis_at_rest = true;
- for (std::size_t e = 0; e < motion_devices.size(); ++e) {
- const auto& device = motions[i][e];
- if (device) {
- std::tie(motion_devices[e].accel, motion_devices[e].gyro,
- motion_devices[e].rotation, motion_devices[e].orientation,
- motion_devices[e].quaternion) = device->GetStatus();
- sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
- }
+ auto& npad = controller.shared_memory_entry;
+ const auto& motion_state = controller.device->GetMotions();
+ auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
+ auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
+ auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
+ auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
+ auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
+ auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
+
+ if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
+ controller.sixaxis_at_rest = true;
+ for (std::size_t e = 0; e < motion_state.size(); ++e) {
+ controller.sixaxis_at_rest =
+ controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
}
- auto& full_sixaxis_entry =
- npad.sixaxis_fullkey.sixaxis[npad.sixaxis_fullkey.common.last_entry_index];
- auto& handheld_sixaxis_entry =
- npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
- auto& dual_left_sixaxis_entry =
- npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
- auto& dual_right_sixaxis_entry =
- npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
- auto& left_sixaxis_entry =
- npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
- auto& right_sixaxis_entry =
- npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
-
switch (controller_type) {
- case NPadControllerType::None:
+ case Core::HID::NpadStyleIndex::None:
UNREACHABLE();
break;
- case NPadControllerType::ProController:
- full_sixaxis_entry.attribute.raw = 0;
- if (sixaxis_sensors_enabled && motions[i][0]) {
- full_sixaxis_entry.attribute.is_connected.Assign(1);
- full_sixaxis_entry.accel = motion_devices[0].accel;
- full_sixaxis_entry.gyro = motion_devices[0].gyro;
- full_sixaxis_entry.rotation = motion_devices[0].rotation;
- full_sixaxis_entry.orientation = motion_devices[0].orientation;
+ case Core::HID::NpadStyleIndex::ProController:
+ sixaxis_fullkey_state.attribute.raw = 0;
+ if (controller.sixaxis_sensor_enabled) {
+ sixaxis_fullkey_state.attribute.is_connected.Assign(1);
+ sixaxis_fullkey_state.accel = motion_state[0].accel;
+ sixaxis_fullkey_state.gyro = motion_state[0].gyro;
+ sixaxis_fullkey_state.rotation = motion_state[0].rotation;
+ sixaxis_fullkey_state.orientation = motion_state[0].orientation;
}
break;
- case NPadControllerType::Handheld:
- handheld_sixaxis_entry.attribute.raw = 0;
- if (sixaxis_sensors_enabled && motions[i][0]) {
- handheld_sixaxis_entry.attribute.is_connected.Assign(1);
- handheld_sixaxis_entry.accel = motion_devices[0].accel;
- handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
- handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
- handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
+ case Core::HID::NpadStyleIndex::Handheld:
+ sixaxis_handheld_state.attribute.raw = 0;
+ if (controller.sixaxis_sensor_enabled) {
+ sixaxis_handheld_state.attribute.is_connected.Assign(1);
+ sixaxis_handheld_state.accel = motion_state[0].accel;
+ sixaxis_handheld_state.gyro = motion_state[0].gyro;
+ sixaxis_handheld_state.rotation = motion_state[0].rotation;
+ sixaxis_handheld_state.orientation = motion_state[0].orientation;
}
break;
- case NPadControllerType::JoyDual:
- dual_left_sixaxis_entry.attribute.raw = 0;
- dual_right_sixaxis_entry.attribute.raw = 0;
- if (sixaxis_sensors_enabled && motions[i][0]) {
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ sixaxis_dual_left_state.attribute.raw = 0;
+ sixaxis_dual_right_state.attribute.raw = 0;
+ if (controller.sixaxis_sensor_enabled) {
// Set motion for the left joycon
- dual_left_sixaxis_entry.attribute.is_connected.Assign(1);
- dual_left_sixaxis_entry.accel = motion_devices[0].accel;
- dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
- dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
- dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
+ sixaxis_dual_left_state.attribute.is_connected.Assign(1);
+ sixaxis_dual_left_state.accel = motion_state[0].accel;
+ sixaxis_dual_left_state.gyro = motion_state[0].gyro;
+ sixaxis_dual_left_state.rotation = motion_state[0].rotation;
+ sixaxis_dual_left_state.orientation = motion_state[0].orientation;
}
- if (sixaxis_sensors_enabled && motions[i][1]) {
+ if (controller.sixaxis_sensor_enabled) {
// Set motion for the right joycon
- dual_right_sixaxis_entry.attribute.is_connected.Assign(1);
- dual_right_sixaxis_entry.accel = motion_devices[1].accel;
- dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
- dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
- dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
+ sixaxis_dual_right_state.attribute.is_connected.Assign(1);
+ sixaxis_dual_right_state.accel = motion_state[1].accel;
+ sixaxis_dual_right_state.gyro = motion_state[1].gyro;
+ sixaxis_dual_right_state.rotation = motion_state[1].rotation;
+ sixaxis_dual_right_state.orientation = motion_state[1].orientation;
}
break;
- case NPadControllerType::JoyLeft:
- left_sixaxis_entry.attribute.raw = 0;
- if (sixaxis_sensors_enabled && motions[i][0]) {
- left_sixaxis_entry.attribute.is_connected.Assign(1);
- left_sixaxis_entry.accel = motion_devices[0].accel;
- left_sixaxis_entry.gyro = motion_devices[0].gyro;
- left_sixaxis_entry.rotation = motion_devices[0].rotation;
- left_sixaxis_entry.orientation = motion_devices[0].orientation;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ sixaxis_left_lifo_state.attribute.raw = 0;
+ if (controller.sixaxis_sensor_enabled) {
+ sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
+ sixaxis_left_lifo_state.accel = motion_state[0].accel;
+ sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
+ sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
+ sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
}
break;
- case NPadControllerType::JoyRight:
- right_sixaxis_entry.attribute.raw = 0;
- if (sixaxis_sensors_enabled && motions[i][1]) {
- right_sixaxis_entry.attribute.is_connected.Assign(1);
- right_sixaxis_entry.accel = motion_devices[1].accel;
- right_sixaxis_entry.gyro = motion_devices[1].gyro;
- right_sixaxis_entry.rotation = motion_devices[1].rotation;
- right_sixaxis_entry.orientation = motion_devices[1].orientation;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ sixaxis_right_lifo_state.attribute.raw = 0;
+ if (controller.sixaxis_sensor_enabled) {
+ sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
+ sixaxis_right_lifo_state.accel = motion_state[1].accel;
+ sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
+ sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
+ sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
}
break;
- case NPadControllerType::GameCube:
- case NPadControllerType::Pokeball:
+ default:
break;
}
+
+ sixaxis_fullkey_state.sampling_number =
+ npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ sixaxis_handheld_state.sampling_number =
+ npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ sixaxis_dual_left_state.sampling_number =
+ npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ sixaxis_dual_right_state.sampling_number =
+ npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ sixaxis_left_lifo_state.sampling_number =
+ npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ sixaxis_right_lifo_state.sampling_number =
+ npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
+
+ if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
+ // This buffer only is updated on handheld on HW
+ npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
+ } else {
+ // Handheld doesn't update this buffer on HW
+ npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
+ }
+
+ npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
+ npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
+ npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
+ npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
+ std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
+ &controller.shared_memory_entry, sizeof(NpadInternalState));
}
- std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
- shared_memory_entries.size() * sizeof(NPadEntry));
}
-void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) {
- style.raw = style_set.raw;
+void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
+ hid_core.SetSupportedStyleTag(style_set);
}
-Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const {
- return style;
+Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
+ return hid_core.GetSupportedStyleTag();
}
void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
@@ -779,11 +673,11 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
return supported_npad_id_types.size();
}
-void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
+void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
hold_type = joy_hold_type;
}
-Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
+Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
return hold_type;
}
@@ -803,29 +697,91 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode;
}
-void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) {
- const std::size_t npad_index = NPadIdToIndex(npad_id);
- ASSERT(npad_index < shared_memory_entries.size());
- if (shared_memory_entries[npad_index].assignment_mode != assignment_mode) {
- shared_memory_entries[npad_index].assignment_mode = assignment_mode;
+void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode) {
+ if (!IsNpadIdValid(npad_id)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
+ return;
+ }
+
+ auto& controller = GetControllerFromNpadIdType(npad_id);
+ if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
+ controller.shared_memory_entry.assignment_mode = assignment_mode;
+ }
+
+ if (!controller.device->IsConnected()) {
+ return;
+ }
+
+ if (assignment_mode == NpadJoyAssignmentMode::Dual) {
+ if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
+ DisconnectNpad(npad_id);
+ controller.is_dual_left_connected = true;
+ controller.is_dual_right_connected = false;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
+ return;
+ }
+ if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
+ DisconnectNpad(npad_id);
+ controller.is_dual_left_connected = false;
+ controller.is_dual_right_connected = true;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
+ return;
+ }
+ return;
+ }
+
+ // This is for NpadJoyAssignmentMode::Single
+
+ // Only JoyconDual get affected by this function
+ if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
+ return;
+ }
+
+ if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
+ DisconnectNpad(npad_id);
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
+ return;
+ }
+ if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
+ DisconnectNpad(npad_id);
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
+ return;
+ }
+
+ // We have two controllers connected to the same npad_id we need to split them
+ const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId();
+ auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
+ DisconnectNpad(npad_id);
+ if (npad_device_type == NpadJoyDeviceType::Left) {
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
+ controller_2.is_dual_left_connected = false;
+ controller_2.is_dual_right_connected = true;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
+ } else {
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
+ controller_2.is_dual_left_connected = true;
+ controller_2.is_dual_right_connected = false;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
}
}
-bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
- const VibrationValue& vibration_value) {
- if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
+bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
+ std::size_t device_index,
+ const Core::HID::VibrationValue& vibration_value) {
+ auto& controller = GetControllerFromNpadIdType(npad_id);
+ if (!controller.device->IsConnected()) {
return false;
}
- const auto& player = Settings::values.players.GetValue()[npad_index];
-
- if (!player.vibration_enabled) {
- if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
- latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
+ if (!controller.device->IsVibrationEnabled()) {
+ if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
+ controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
// Send an empty vibration to stop any vibrations.
- vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
+ Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
+ controller.device->SetVibration(device_index, vibration);
// Then reset the vibration value to its default value.
- latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE;
+ controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE;
}
return false;
@@ -839,27 +795,25 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size
const auto now = steady_clock::now();
// Filter out non-zero vibrations that are within 10ms of each other.
- if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) &&
- duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) <
+ if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
+ duration_cast<milliseconds>(
+ now - controller.vibration[device_index].last_vibration_timepoint) <
milliseconds(10)) {
return false;
}
- last_vibration_timepoints[npad_index][device_index] = now;
+ controller.vibration[device_index].last_vibration_timepoint = now;
}
- auto& vibration = vibrations[npad_index][device_index];
- const auto player_vibration_strength = static_cast<f32>(player.vibration_strength);
- const auto amp_low =
- std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f);
- const auto amp_high =
- std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
- return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
- vibration_value.freq_high);
+ Core::HID::VibrationValue vibration{
+ vibration_value.low_amplitude, vibration_value.low_frequency,
+ vibration_value.high_amplitude, vibration_value.high_frequency};
+ return controller.device->SetVibration(device_index, vibration);
}
-void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle,
- const VibrationValue& vibration_value) {
+void Controller_NPad::VibrateController(
+ const Core::HID::VibrationDeviceHandle& vibration_device_handle,
+ const Core::HID::VibrationValue& vibration_value) {
if (!IsDeviceHandleValid(vibration_device_handle)) {
return;
}
@@ -868,42 +822,45 @@ void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_han
return;
}
- const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ auto& controller = GetControllerFromHandle(vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
- if (!vibration_devices_mounted[npad_index][device_index] ||
- !connected_controllers[npad_index].is_connected) {
+ if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
return;
}
- if (vibration_device_handle.device_index == DeviceIndex::None) {
+ if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
UNREACHABLE_MSG("DeviceIndex should never be None!");
return;
}
// Some games try to send mismatched parameters in the device handle, block these.
- if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
- (vibration_device_handle.npad_type == NpadType::JoyconRight ||
- vibration_device_handle.device_index == DeviceIndex::Right)) ||
- (connected_controllers[npad_index].type == NPadControllerType::JoyRight &&
- (vibration_device_handle.npad_type == NpadType::JoyconLeft ||
- vibration_device_handle.device_index == DeviceIndex::Left))) {
+ if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
+ (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight ||
+ vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) ||
+ (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight &&
+ (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft ||
+ vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) {
return;
}
// Filter out vibrations with equivalent values to reduce unnecessary state changes.
- if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low &&
- vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) {
+ if (vibration_value.low_amplitude ==
+ controller.vibration[device_index].latest_vibration_value.low_amplitude &&
+ vibration_value.high_amplitude ==
+ controller.vibration[device_index].latest_vibration_value.high_amplitude) {
return;
}
- if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) {
- latest_vibration_values[npad_index][device_index] = vibration_value;
+ if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index,
+ vibration_value)) {
+ controller.vibration[device_index].latest_vibration_value = vibration_value;
}
}
-void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
- const std::vector<VibrationValue>& vibration_values) {
+void Controller_NPad::VibrateControllers(
+ const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
+ const std::vector<Core::HID::VibrationValue>& vibration_values) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}
@@ -918,168 +875,285 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat
}
}
-Controller_NPad::VibrationValue Controller_NPad::GetLastVibration(
- const DeviceHandle& vibration_device_handle) const {
+Core::HID::VibrationValue Controller_NPad::GetLastVibration(
+ const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
if (!IsDeviceHandleValid(vibration_device_handle)) {
return {};
}
- const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto& controller = GetControllerFromHandle(vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
- return latest_vibration_values[npad_index][device_index];
+ return controller.vibration[device_index].latest_vibration_value;
}
-void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) {
+void Controller_NPad::InitializeVibrationDevice(
+ const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
if (!IsDeviceHandleValid(vibration_device_handle)) {
return;
}
- const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
InitializeVibrationDeviceAtIndex(npad_index, device_index);
}
-void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
+void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
std::size_t device_index) {
+ auto& controller = GetControllerFromNpadIdType(npad_id);
if (!Settings::values.vibration_enabled.GetValue()) {
- vibration_devices_mounted[npad_index][device_index] = false;
+ controller.vibration[device_index].device_mounted = false;
return;
}
- if (vibrations[npad_index][device_index]) {
- vibration_devices_mounted[npad_index][device_index] =
- vibrations[npad_index][device_index]->GetStatus() == 1;
- } else {
- vibration_devices_mounted[npad_index][device_index] = false;
- }
+ controller.vibration[device_index].device_mounted =
+ controller.device->TestVibration(device_index);
}
void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
permit_vibration_session_enabled = permit_vibration_session;
}
-bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const {
+bool Controller_NPad::IsVibrationDeviceMounted(
+ const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
if (!IsDeviceHandleValid(vibration_device_handle)) {
return false;
}
- const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto& controller = GetControllerFromHandle(vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
- return vibration_devices_mounted[npad_index][device_index];
+ return controller.vibration[device_index].device_mounted;
}
-Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) {
- return styleset_changed_events[NPadIdToIndex(npad_id)]->GetReadableEvent();
+Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
+ if (!IsNpadIdValid(npad_id)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
+ // Fallback to player 1
+ const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1);
+ return controller.styleset_changed_event->GetReadableEvent();
+ }
+
+ const auto& controller = GetControllerFromNpadIdType(npad_id);
+ return controller.styleset_changed_event->GetReadableEvent();
}
-void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
- styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent().Signal();
+void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
+ const auto& controller = GetControllerFromNpadIdType(npad_id);
+ controller.styleset_changed_event->GetWritableEvent().Signal();
}
-void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
- UpdateControllerAt(controller, npad_index, true);
+void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
+ Core::HID::NpadIdType npad_id) {
+ UpdateControllerAt(controller, npad_id, true);
}
-void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
- bool connected) {
+void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
+ Core::HID::NpadIdType npad_id, bool connected) {
+ auto& controller = GetControllerFromNpadIdType(npad_id);
if (!connected) {
- DisconnectNpadAtIndex(npad_index);
- return;
- }
-
- if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) {
- Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
- MapNPadToSettingsType(controller);
- Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
- connected_controllers[HANDHELD_INDEX] = {controller, true};
- InitNewlyAddedController(HANDHELD_INDEX);
+ DisconnectNpad(npad_id);
return;
}
- Settings::values.players.GetValue()[npad_index].controller_type =
- MapNPadToSettingsType(controller);
- Settings::values.players.GetValue()[npad_index].connected = true;
- connected_controllers[npad_index] = {controller, true};
- InitNewlyAddedController(npad_index);
+ controller.device->SetNpadStyleIndex(type);
+ InitNewlyAddedController(npad_id);
}
-void Controller_NPad::DisconnectNpad(u32 npad_id) {
- DisconnectNpadAtIndex(NPadIdToIndex(npad_id));
-}
+void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
+ if (!IsNpadIdValid(npad_id)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
+ return;
+ }
-void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
- for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) {
+ LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
+ auto& controller = GetControllerFromNpadIdType(npad_id);
+ for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
// Send an empty vibration to stop any vibrations.
- VibrateControllerAtIndex(npad_index, device_idx, {});
- vibration_devices_mounted[npad_index][device_idx] = false;
+ VibrateControllerAtIndex(npad_id, device_idx, {});
+ controller.vibration[device_idx].device_mounted = false;
}
- Settings::values.players.GetValue()[npad_index].connected = false;
- connected_controllers[npad_index].is_connected = false;
-
- auto& controller = shared_memory_entries[npad_index];
- controller.style_set.raw = 0; // Zero out
- controller.device_type.raw = 0;
- controller.system_properties.raw = 0;
- controller.button_properties.raw = 0;
- controller.battery_level_dual = 0;
- controller.battery_level_left = 0;
- controller.battery_level_right = 0;
- controller.fullkey_color = {};
- controller.joycon_color = {};
- controller.assignment_mode = NpadAssignments::Dual;
- controller.footer_type = AppletFooterUiType::None;
+ auto& shared_memory_entry = controller.shared_memory_entry;
+ // Don't reset shared_memory_entry.assignment_mode this value is persistent
+ shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
+ shared_memory_entry.device_type.raw = 0;
+ shared_memory_entry.system_properties.raw = 0;
+ shared_memory_entry.button_properties.raw = 0;
+ shared_memory_entry.battery_level_dual = 0;
+ shared_memory_entry.battery_level_left = 0;
+ shared_memory_entry.battery_level_right = 0;
+ shared_memory_entry.fullkey_color = {
+ .attribute = ColorAttribute::NoController,
+ .fullkey = {},
+ };
+ shared_memory_entry.joycon_color = {
+ .attribute = ColorAttribute::NoController,
+ .left = {},
+ .right = {},
+ };
+ shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
+
+ controller.is_dual_left_connected = true;
+ controller.is_dual_right_connected = true;
+ controller.is_connected = false;
+ controller.device->Disconnect();
+ SignalStyleSetChangedEvent(npad_id);
+ WriteEmptyEntry(controller.shared_memory_entry);
+}
- SignalStyleSetChangedEvent(IndexToNPad(npad_index));
+void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ GyroscopeZeroDriftMode drift_mode) {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ return;
+ }
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ controller.gyroscope_zero_drift_mode = drift_mode;
}
-void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
- gyroscope_zero_drift_mode = drift_mode;
+Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode(
+ Core::HID::SixAxisSensorHandle sixaxis_handle) const {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ // Return the default value
+ return GyroscopeZeroDriftMode::Standard;
+ }
+ const auto& controller = GetControllerFromHandle(sixaxis_handle);
+ return controller.gyroscope_zero_drift_mode;
}
-Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const {
- return gyroscope_zero_drift_mode;
+bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ // Return the default value
+ return true;
+ }
+ const auto& controller = GetControllerFromHandle(sixaxis_handle);
+ return controller.sixaxis_at_rest;
}
-bool Controller_NPad::IsSixAxisSensorAtRest() const {
- return sixaxis_at_rest;
+void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool sixaxis_status) {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ return;
+ }
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ controller.sixaxis_sensor_enabled = sixaxis_status;
}
-void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
- sixaxis_sensors_enabled = six_axis_status;
+void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool sixaxis_fusion_status) {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ return;
+ }
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ controller.sixaxis_fusion_enabled = sixaxis_fusion_status;
}
-void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) {
- sixaxis_fusion_parameter1 = parameter1;
- sixaxis_fusion_parameter2 = parameter2;
+void Controller_NPad::SetSixAxisFusionParameters(
+ Core::HID::SixAxisSensorHandle sixaxis_handle,
+ Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ return;
+ }
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ controller.sixaxis_fusion = sixaxis_fusion_parameters;
}
-std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() {
- return {
- sixaxis_fusion_parameter1,
- sixaxis_fusion_parameter2,
- };
+Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters(
+ Core::HID::SixAxisSensorHandle sixaxis_handle) {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ // Since these parameters are unknow just return zeros
+ return {};
+ }
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ return controller.sixaxis_fusion;
}
-void Controller_NPad::ResetSixAxisFusionParameters() {
- sixaxis_fusion_parameter1 = 0.0f;
- sixaxis_fusion_parameter2 = 0.0f;
+void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) {
+ if (!IsDeviceHandleValid(sixaxis_handle)) {
+ LOG_ERROR(Service_HID, "Invalid handle");
+ return;
+ }
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ // Since these parameters are unknow just fill with zeros
+ controller.sixaxis_fusion = {};
}
-void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
- const auto npad_index_1 = NPadIdToIndex(npad_id_1);
- const auto npad_index_2 = NPadIdToIndex(npad_id_2);
+void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
+ if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
+ npad_id_2);
+ return;
+ }
+ auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
+ auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
+ const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
+ const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
+ bool merge_controllers = false;
// If the controllers at both npad indices form a pair of left and right joycons, merge them.
// Otherwise, do nothing.
- if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft &&
- connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) ||
- (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
- connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
+ controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
+ controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
+ !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
+ controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+
+ if (merge_controllers) {
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNpad(npad_id_2);
- AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
+ controller_1.is_dual_left_connected = true;
+ controller_1.is_dual_right_connected = true;
+ AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
+ return;
}
+ LOG_WARNING(Service_HID,
+ "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
+ "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
+ npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
+ controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
+ controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
+ controller_2.is_dual_right_connected);
}
void Controller_NPad::StartLRAssignmentMode() {
@@ -1092,61 +1166,66 @@ void Controller_NPad::StopLRAssignmentMode() {
is_in_lr_assignment_mode = false;
}
-bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
- if (npad_id_1 == NPAD_HANDHELD || npad_id_2 == NPAD_HANDHELD || npad_id_1 == NPAD_UNKNOWN ||
- npad_id_2 == NPAD_UNKNOWN) {
+bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
+ if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
+ npad_id_2);
+ return false;
+ }
+ if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
+ npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
+ npad_id_2 == Core::HID::NpadIdType::Other) {
return true;
}
- const auto npad_index_1 = NPadIdToIndex(npad_id_1);
- const auto npad_index_2 = NPadIdToIndex(npad_id_2);
-
- if (!IsControllerSupported(connected_controllers[npad_index_1].type) ||
- !IsControllerSupported(connected_controllers[npad_index_2].type)) {
+ const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
+ const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
+ const auto type_index_1 = controller_1->GetNpadStyleIndex();
+ const auto type_index_2 = controller_2->GetNpadStyleIndex();
+ const auto is_connected_1 = controller_1->IsConnected();
+ const auto is_connected_2 = controller_2->IsConnected();
+
+ if (!IsControllerSupported(type_index_1) && is_connected_1) {
+ return false;
+ }
+ if (!IsControllerSupported(type_index_2) && is_connected_2) {
return false;
}
- std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
-
- AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
- AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
+ UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
+ UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
return true;
}
-Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
- if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
- // These are controllers without led patterns
- return LedPattern{0, 0, 0, 0};
- }
- switch (npad_id) {
- case 0:
- return LedPattern{1, 0, 0, 0};
- case 1:
- return LedPattern{1, 1, 0, 0};
- case 2:
- return LedPattern{1, 1, 1, 0};
- case 3:
- return LedPattern{1, 1, 1, 1};
- case 4:
- return LedPattern{1, 0, 0, 1};
- case 5:
- return LedPattern{1, 0, 1, 0};
- case 6:
- return LedPattern{1, 0, 1, 1};
- case 7:
- return LedPattern{0, 1, 1, 0};
- default:
- return LedPattern{0, 0, 0, 0};
+Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
+ if (!IsNpadIdValid(npad_id)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
+ return Core::HID::LedPattern{0, 0, 0, 0};
}
+ const auto& controller = GetControllerFromNpadIdType(npad_id).device;
+ return controller->GetLedPattern();
}
-bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const {
- return unintended_home_button_input_protection[NPadIdToIndex(npad_id)];
+bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
+ Core::HID::NpadIdType npad_id) const {
+ if (!IsNpadIdValid(npad_id)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
+ // Return the default value
+ return false;
+ }
+ const auto& controller = GetControllerFromNpadIdType(npad_id);
+ return controller.unintended_home_button_input_protection;
}
void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
- u32 npad_id) {
- unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
+ Core::HID::NpadIdType npad_id) {
+ if (!IsNpadIdValid(npad_id)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
+ return;
+ }
+ auto& controller = GetControllerFromNpadIdType(npad_id);
+ controller.unintended_home_button_input_protection = is_protection_enabled;
}
void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1154,49 +1233,51 @@ void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
}
void Controller_NPad::ClearAllConnectedControllers() {
- for (auto& controller : connected_controllers) {
- if (controller.is_connected && controller.type != NPadControllerType::None) {
- controller.type = NPadControllerType::None;
- controller.is_connected = false;
+ for (auto& controller : controller_data) {
+ if (controller.device->IsConnected() &&
+ controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
+ controller.device->Disconnect();
+ controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
}
}
}
void Controller_NPad::DisconnectAllConnectedControllers() {
- for (auto& controller : connected_controllers) {
- controller.is_connected = false;
+ for (auto& controller : controller_data) {
+ controller.device->Disconnect();
}
}
void Controller_NPad::ConnectAllDisconnectedControllers() {
- for (auto& controller : connected_controllers) {
- if (controller.type != NPadControllerType::None && !controller.is_connected) {
- controller.is_connected = true;
+ for (auto& controller : controller_data) {
+ if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
+ !controller.device->IsConnected()) {
+ controller.device->Connect();
}
}
}
void Controller_NPad::ClearAllControllers() {
- for (auto& controller : connected_controllers) {
- controller.type = NPadControllerType::None;
- controller.is_connected = false;
+ for (auto& controller : controller_data) {
+ controller.device->Disconnect();
+ controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
}
}
-u32 Controller_NPad::GetAndResetPressState() {
- return press_state.exchange(0);
+Core::HID::NpadButton Controller_NPad::GetAndResetPressState() {
+ return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
}
-bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
- if (controller == NPadControllerType::Handheld) {
+bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
+ if (controller == Core::HID::NpadStyleIndex::Handheld) {
const bool support_handheld =
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
- NPAD_HANDHELD) != supported_npad_id_types.end();
+ Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end();
// Handheld is not even a supported type, lets stop here
if (!support_handheld) {
return false;
}
- // Handheld should not be supported in docked mode
+ // Handheld shouldn't be supported in docked mode
if (Settings::values.use_docked_mode.GetValue()) {
return false;
}
@@ -1205,20 +1286,31 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
}
if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
- [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
+ [](Core::HID::NpadIdType npad_id) {
+ return npad_id <= Core::HID::NpadIdType::Player8;
+ })) {
+ Core::HID::NpadStyleTag style = GetSupportedStyleSet();
switch (controller) {
- case NPadControllerType::ProController:
+ case Core::HID::NpadStyleIndex::ProController:
return style.fullkey;
- case NPadControllerType::JoyDual:
+ case Core::HID::NpadStyleIndex::JoyconDual:
return style.joycon_dual;
- case NPadControllerType::JoyLeft:
+ case Core::HID::NpadStyleIndex::JoyconLeft:
return style.joycon_left;
- case NPadControllerType::JoyRight:
+ case Core::HID::NpadStyleIndex::JoyconRight:
return style.joycon_right;
- case NPadControllerType::GameCube:
+ case Core::HID::NpadStyleIndex::GameCube:
return style.gamecube;
- case NPadControllerType::Pokeball:
+ case Core::HID::NpadStyleIndex::Pokeball:
return style.palma;
+ case Core::HID::NpadStyleIndex::NES:
+ return style.lark;
+ case Core::HID::NpadStyleIndex::SNES:
+ return style.lucia;
+ case Core::HID::NpadStyleIndex::N64:
+ return style.lagoon;
+ case Core::HID::NpadStyleIndex::SegaGenesis:
+ return style.lager;
default:
return false;
}
@@ -1227,4 +1319,48 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return false;
}
+Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
+ const Core::HID::SixAxisSensorHandle& device_handle) {
+ const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
+ return GetControllerFromNpadIdType(npad_id);
+}
+
+const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
+ const Core::HID::SixAxisSensorHandle& device_handle) const {
+ const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
+ return GetControllerFromNpadIdType(npad_id);
+}
+
+Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
+ const Core::HID::VibrationDeviceHandle& device_handle) {
+ const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
+ return GetControllerFromNpadIdType(npad_id);
+}
+
+const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
+ const Core::HID::VibrationDeviceHandle& device_handle) const {
+ const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
+ return GetControllerFromNpadIdType(npad_id);
+}
+
+Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
+ Core::HID::NpadIdType npad_id) {
+ if (!IsNpadIdValid(npad_id)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
+ npad_id = Core::HID::NpadIdType::Player1;
+ }
+ const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
+ return controller_data[npad_index];
+}
+
+const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
+ Core::HID::NpadIdType npad_id) const {
+ if (!IsNpadIdValid(npad_id)) {
+ LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
+ npad_id = Core::HID::NpadIdType::Player1;
+ }
+ const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
+ return controller_data[npad_index];
+}
+
} // 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 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/quaternion.h"
-#include "common/settings.h"
-#include "core/frontend/input.h"
+#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/ring_lifo.h"
+
+namespace Core::HID {
+class EmulatedController;
+enum class ControllerTriggerType;
+} // namespace Core::HID
namespace Kernel {
class KEvent;
@@ -26,12 +31,9 @@ class ServiceContext;
namespace Service::HID {
-constexpr u32 NPAD_HANDHELD = 32;
-constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
-
class Controller_NPad final : public ControllerBase {
public:
- explicit Controller_NPad(Core::System& system_,
+ explicit Controller_NPad(Core::HID::HIDCore& hid_core_,
KernelHelpers::ServiceContext& service_context_);
~Controller_NPad() override;
@@ -48,60 +50,39 @@ public:
void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
- enum class NPadControllerType {
- None,
- ProController,
- Handheld,
- JoyDual,
- JoyLeft,
- JoyRight,
- GameCube,
- Pokeball,
- };
-
- enum class NpadType : u8 {
- ProController = 3,
- Handheld = 4,
- JoyconDual = 5,
- JoyconLeft = 6,
- JoyconRight = 7,
- GameCube = 8,
- Pokeball = 9,
- MaxNpadType = 10,
- };
-
- enum class DeviceIndex : u8 {
- Left = 0,
- Right = 1,
- None = 2,
- MaxDeviceIndex = 3,
- };
-
+ // This is nn::hid::GyroscopeZeroDriftMode
enum class GyroscopeZeroDriftMode : u32 {
Loose = 0,
Standard = 1,
Tight = 2,
};
- enum class NpadHoldType : u64 {
+ // This is nn::hid::NpadJoyHoldType
+ enum class NpadJoyHoldType : u64 {
Vertical = 0,
Horizontal = 1,
};
- enum class NpadAssignments : u32 {
+ // This is nn::hid::NpadJoyAssignmentMode
+ enum class NpadJoyAssignmentMode : u32 {
Dual = 0,
Single = 1,
};
+ // This is nn::hid::NpadJoyDeviceType
+ enum class NpadJoyDeviceType : s64 {
+ Left = 0,
+ Right = 1,
+ };
+
+ // This is nn::hid::NpadHandheldActivationMode
enum class NpadHandheldActivationMode : u64 {
Dual = 0,
Single = 1,
None = 2,
};
+ // This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
Mode_5ms = 0,
Mode_10ms = 1,
@@ -109,74 +90,22 @@ public:
Default = 3,
};
- struct DeviceHandle {
- NpadType npad_type;
- u8 npad_id;
- DeviceIndex device_index;
- INSERT_PADDING_BYTES_NOINIT(1);
+ static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
+ .low_amplitude = 0.0f,
+ .low_frequency = 160.0f,
+ .high_amplitude = 0.0f,
+ .high_frequency = 320.0f,
};
- static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
- struct NpadStyleSet {
- union {
- u32_le raw{};
-
- BitField<0, 1, u32> fullkey;
- BitField<1, 1, u32> handheld;
- BitField<2, 1, u32> joycon_dual;
- BitField<3, 1, u32> joycon_left;
- BitField<4, 1, u32> joycon_right;
- BitField<5, 1, u32> gamecube;
- BitField<6, 1, u32> palma;
- BitField<7, 1, u32> lark;
- BitField<8, 1, u32> handheld_lark;
- BitField<9, 1, u32> lucia;
- BitField<29, 1, u32> system_ext;
- BitField<30, 1, u32> system;
- };
- };
- static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
-
- struct VibrationValue {
- f32 amp_low;
- f32 freq_low;
- f32 amp_high;
- f32 freq_high;
- };
- static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
-
- static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
- .amp_low = 0.0f,
- .freq_low = 160.0f,
- .amp_high = 0.0f,
- .freq_high = 320.0f,
- };
-
- struct LedPattern {
- explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
- position1.Assign(light1);
- position2.Assign(light2);
- position3.Assign(light3);
- position4.Assign(light4);
- }
- union {
- u64 raw{};
- BitField<0, 1, u64> position1;
- BitField<1, 1, u64> position2;
- BitField<2, 1, u64> position3;
- BitField<3, 1, u64> position4;
- };
- };
-
- void SetSupportedStyleSet(NpadStyleSet style_set);
- NpadStyleSet GetSupportedStyleSet() const;
+ void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
+ Core::HID::NpadStyleTag GetSupportedStyleSet() const;
void SetSupportedNpadIdTypes(u8* data, std::size_t length);
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
std::size_t GetSupportedNpadIdTypesSize() const;
- void SetHoldType(NpadHoldType joy_hold_type);
- NpadHoldType GetHoldType() const;
+ void SetHoldType(NpadJoyHoldType joy_hold_type);
+ NpadJoyHoldType GetHoldType() const;
void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
@@ -184,162 +113,107 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
- void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
+ void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode);
- bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
- const VibrationValue& vibration_value);
+ bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
+ const Core::HID::VibrationValue& vibration_value);
- void VibrateController(const DeviceHandle& vibration_device_handle,
- const VibrationValue& vibration_value);
+ void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
+ const Core::HID::VibrationValue& vibration_value);
- void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
- const std::vector<VibrationValue>& vibration_values);
+ void VibrateControllers(
+ const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
+ const std::vector<Core::HID::VibrationValue>& vibration_values);
- VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const;
+ Core::HID::VibrationValue GetLastVibration(
+ const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
- void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle);
+ void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
- void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index);
+ void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index);
void SetPermitVibrationSession(bool permit_vibration_session);
- bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
+ bool IsVibrationDeviceMounted(
+ const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
- Kernel::KReadableEvent& GetStyleSetChangedEvent(u32 npad_id);
- void SignalStyleSetChangedEvent(u32 npad_id) const;
+ Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id);
+ void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const;
// Adds a new controller at an index.
- void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
+ void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id);
// Adds a new controller at an index with connection status.
- void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
-
- void DisconnectNpad(u32 npad_id);
- void DisconnectNpadAtIndex(std::size_t index);
-
- void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
- GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
- bool IsSixAxisSensorAtRest() const;
- void SetSixAxisEnabled(bool six_axis_status);
- void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2);
- std::pair<f32, f32> GetSixAxisFusionParameters();
- void ResetSixAxisFusionParameters();
- LedPattern GetLedPattern(u32 npad_id);
- bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
- void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
+ void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
+ bool connected);
+
+ void DisconnectNpad(Core::HID::NpadIdType npad_id);
+
+ void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ GyroscopeZeroDriftMode drift_mode);
+ GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode(
+ Core::HID::SixAxisSensorHandle sixaxis_handle) const;
+ bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const;
+ void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status);
+ void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ bool sixaxis_fusion_status);
+ void SetSixAxisFusionParameters(
+ Core::HID::SixAxisSensorHandle sixaxis_handle,
+ Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
+ Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters(
+ Core::HID::SixAxisSensorHandle sixaxis_handle);
+ void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle);
+ Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
+ bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
+ void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
+ Core::HID::NpadIdType npad_id);
void SetAnalogStickUseCenterClamp(bool use_center_clamp);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
- void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2);
+ void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
void StartLRAssignmentMode();
void StopLRAssignmentMode();
- bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2);
+ bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
- u32 GetAndResetPressState();
+ Core::HID::NpadButton GetAndResetPressState();
- static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
- static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
- static std::size_t NPadIdToIndex(u32 npad_id);
- static u32 IndexToNPad(std::size_t index);
- static bool IsNpadIdValid(u32 npad_id);
- static bool IsDeviceHandleValid(const DeviceHandle& device_handle);
+ static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
+ static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
+ static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
private:
- struct CommonHeader {
- s64_le timestamp;
- s64_le total_entry_count;
- s64_le last_entry_index;
- s64_le entry_count;
- };
- static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
-
- enum class ColorAttributes : u32_le {
+ // This is nn::hid::detail::ColorAttribute
+ enum class ColorAttribute : u32 {
Ok = 0,
ReadError = 1,
NoController = 2,
};
- static_assert(sizeof(ColorAttributes) == 4, "ColorAttributes is an invalid size");
+ static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
- struct ControllerColor {
- u32_le body;
- u32_le button;
+ // This is nn::hid::detail::NpadFullKeyColorState
+ struct NpadFullKeyColorState {
+ ColorAttribute attribute;
+ Core::HID::NpadControllerColor fullkey;
};
- static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
+ static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
- struct FullKeyColor {
- ColorAttributes attribute;
- ControllerColor fullkey;
+ // This is nn::hid::detail::NpadJoyColorState
+ struct NpadJoyColorState {
+ ColorAttribute attribute;
+ Core::HID::NpadControllerColor left;
+ Core::HID::NpadControllerColor right;
};
- static_assert(sizeof(FullKeyColor) == 0xC, "FullKeyColor is an invalid size");
+ static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
- struct JoyconColor {
- ColorAttributes attribute;
- ControllerColor left;
- ControllerColor right;
- };
- static_assert(sizeof(JoyconColor) == 0x14, "JoyconColor is an invalid size");
-
- struct ControllerPadState {
- union {
- u64_le raw{};
- // Button states
- BitField<0, 1, u64> a;
- BitField<1, 1, u64> b;
- BitField<2, 1, u64> x;
- BitField<3, 1, u64> y;
- BitField<4, 1, u64> l_stick;
- BitField<5, 1, u64> r_stick;
- BitField<6, 1, u64> l;
- BitField<7, 1, u64> r;
- BitField<8, 1, u64> zl;
- BitField<9, 1, u64> zr;
- BitField<10, 1, u64> plus;
- BitField<11, 1, u64> minus;
-
- // D-Pad
- BitField<12, 1, u64> d_left;
- BitField<13, 1, u64> d_up;
- BitField<14, 1, u64> d_right;
- BitField<15, 1, u64> d_down;
-
- // Left JoyStick
- BitField<16, 1, u64> l_stick_left;
- BitField<17, 1, u64> l_stick_up;
- BitField<18, 1, u64> l_stick_right;
- BitField<19, 1, u64> l_stick_down;
-
- // Right JoyStick
- BitField<20, 1, u64> r_stick_left;
- BitField<21, 1, u64> r_stick_up;
- BitField<22, 1, u64> r_stick_right;
- BitField<23, 1, u64> r_stick_down;
-
- // Not always active?
- BitField<24, 1, u64> left_sl;
- BitField<25, 1, u64> left_sr;
-
- BitField<26, 1, u64> right_sl;
- BitField<27, 1, u64> right_sr;
-
- BitField<28, 1, u64> palma;
- BitField<30, 1, u64> handheld_left_b;
- };
- };
- static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
-
- struct AnalogPosition {
- s32_le x;
- s32_le y;
- };
- static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
-
- struct ConnectionState {
+ // This is nn::hid::NpadAttribute
+ struct NpadAttribute {
union {
- u32_le raw{};
+ u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
@@ -348,79 +222,60 @@ private:
BitField<5, 1, u32> is_right_wired;
};
};
- static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
-
- struct ControllerPad {
- ControllerPadState pad_states;
- AnalogPosition l_stick;
- AnalogPosition r_stick;
- };
- static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
-
- struct GenericStates {
- s64_le timestamp;
- s64_le timestamp2;
- ControllerPad pad;
- ConnectionState connection_status;
- };
- static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
-
- struct NPadGeneric {
- CommonHeader common;
- std::array<GenericStates, 17> npad;
+ static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
+
+ // This is nn::hid::NpadFullKeyState
+ // This is nn::hid::NpadHandheldState
+ // This is nn::hid::NpadJoyDualState
+ // This is nn::hid::NpadJoyLeftState
+ // This is nn::hid::NpadJoyRightState
+ // This is nn::hid::NpadPalmaState
+ // This is nn::hid::NpadSystemExtState
+ struct NPadGenericState {
+ s64_le sampling_number;
+ Core::HID::NpadButtonState npad_buttons;
+ Core::HID::AnalogStickState l_stick;
+ Core::HID::AnalogStickState r_stick;
+ NpadAttribute connection_status;
+ INSERT_PADDING_BYTES(4); // Reserved
};
- static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
+ static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
- struct SixAxisAttributes {
+ // This is nn::hid::SixAxisSensorAttribute
+ struct SixAxisSensorAttribute {
union {
- u32_le raw{};
+ u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_interpolated;
};
};
- static_assert(sizeof(SixAxisAttributes) == 4, "SixAxisAttributes is an invalid size");
+ static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
- struct SixAxisStates {
- s64_le timestamp{};
- INSERT_PADDING_WORDS(2);
- s64_le timestamp2{};
+ // This is nn::hid::SixAxisSensorState
+ struct SixAxisSensorState {
+ s64 delta_time{};
+ s64 sampling_number{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
- SixAxisAttributes attribute;
+ SixAxisSensorAttribute attribute;
INSERT_PADDING_BYTES(4); // Reserved
};
- static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
-
- struct SixAxisGeneric {
- CommonHeader common{};
- std::array<SixAxisStates, 17> sixaxis{};
- };
- static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
+ static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
- struct TriggerState {
- s64_le timestamp{};
- s64_le timestamp2{};
- s32_le l_analog{};
- s32_le r_analog{};
+ // This is nn::hid::server::NpadGcTriggerState
+ struct NpadGcTriggerState {
+ s64 sampling_number{};
+ s32 l_analog{};
+ s32 r_analog{};
};
- static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size");
-
- struct TriggerGeneric {
- INSERT_PADDING_BYTES(0x4);
- s64_le timestamp;
- INSERT_PADDING_BYTES(0x4);
- s64_le total_entry_count;
- s64_le last_entry_index;
- s64_le entry_count;
- std::array<TriggerState, 17> trigger{};
- };
- static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
+ static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
+ // This is nn::hid::NpadSystemProperties
struct NPadSystemProperties {
union {
- s64_le raw{};
+ s64 raw{};
BitField<0, 1, s64> is_charging_joy_dual;
BitField<1, 1, s64> is_charging_joy_left;
BitField<2, 1, s64> is_charging_joy_right;
@@ -438,17 +293,20 @@ private:
};
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
- struct NPadButtonProperties {
+ // This is nn::hid::NpadSystemButtonProperties
+ struct NpadSystemButtonProperties {
union {
- s32_le raw{};
+ s32 raw{};
BitField<0, 1, s32> is_home_button_protection_enabled;
};
};
- static_assert(sizeof(NPadButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
+ static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
+ "NPadButtonProperties is an invalid size");
- struct NPadDevice {
+ // This is nn::hid::system::DeviceType
+ struct DeviceType {
union {
- u32_le raw{};
+ u32 raw{};
BitField<0, 1, s32> fullkey;
BitField<1, 1, s32> debug_pad;
BitField<2, 1, s32> handheld_left;
@@ -465,26 +323,29 @@ private:
BitField<13, 1, s32> handheld_lark_nes_left;
BitField<14, 1, s32> handheld_lark_nes_right;
BitField<15, 1, s32> lucia;
+ BitField<16, 1, s32> lagon;
+ BitField<17, 1, s32> lager;
BitField<31, 1, s32> system;
};
};
- struct MotionDevice {
- Common::Vec3f accel;
- Common::Vec3f gyro;
- Common::Vec3f rotation;
- std::array<Common::Vec3f, 3> orientation;
- Common::Quaternion<f32> quaternion;
- };
-
- struct NfcXcdHandle {
- INSERT_PADDING_BYTES(0x60);
+ // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
+ struct NfcXcdDeviceHandleStateImpl {
+ u64 handle;
+ bool is_available;
+ bool is_activated;
+ INSERT_PADDING_BYTES(0x6); // Reserved
+ u64 sampling_number;
};
+ static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
+ "NfcXcdDeviceHandleStateImpl is an invalid size");
+ // This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
+ // This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
@@ -510,95 +371,153 @@ private:
Lagon = 21,
};
- struct NPadEntry {
- NpadStyleSet style_set;
- NpadAssignments assignment_mode;
- FullKeyColor fullkey_color;
- JoyconColor joycon_color;
-
- NPadGeneric fullkey_states;
- NPadGeneric handheld_states;
- NPadGeneric joy_dual_states;
- NPadGeneric joy_left_states;
- NPadGeneric joy_right_states;
- NPadGeneric palma_states;
- NPadGeneric system_ext_states;
- SixAxisGeneric sixaxis_fullkey;
- SixAxisGeneric sixaxis_handheld;
- SixAxisGeneric sixaxis_dual_left;
- SixAxisGeneric sixaxis_dual_right;
- SixAxisGeneric sixaxis_left;
- SixAxisGeneric sixaxis_right;
- NPadDevice device_type;
- INSERT_PADDING_BYTES(0x4); // reserved
+ struct AppletFooterUi {
+ AppletFooterUiAttributes attributes;
+ AppletFooterUiType type;
+ INSERT_PADDING_BYTES(0x5B); // Reserved
+ };
+ static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
+
+ // This is nn::hid::NpadLarkType
+ enum class NpadLarkType : u32 {
+ Invalid,
+ H1,
+ H2,
+ NL,
+ NR,
+ };
+
+ // This is nn::hid::NpadLuciaType
+ enum class NpadLuciaType : u32 {
+ Invalid,
+ J,
+ E,
+ U,
+ };
+
+ // This is nn::hid::NpadLagonType
+ enum class NpadLagonType : u32 {
+ Invalid,
+ };
+
+ // This is nn::hid::NpadLagerType
+ enum class NpadLagerType : u32 {
+ Invalid,
+ J,
+ E,
+ U,
+ };
+
+ // This is nn::hid::detail::NpadInternalState
+ struct NpadInternalState {
+ Core::HID::NpadStyleTag style_tag;
+ NpadJoyAssignmentMode assignment_mode;
+ NpadFullKeyColorState fullkey_color;
+ NpadJoyColorState joycon_color;
+ Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
+ Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
+ Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
+ Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
+ Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
+ Lifo<NPadGenericState, hid_entry_count> palma_lifo;
+ Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
+ DeviceType device_type;
+ INSERT_PADDING_BYTES(0x4); // Reserved
NPadSystemProperties system_properties;
- NPadButtonProperties button_properties;
- u32 battery_level_dual;
- u32 battery_level_left;
- u32 battery_level_right;
- AppletFooterUiAttributes footer_attributes;
- AppletFooterUiType footer_type;
- // nfc_states needs to be checked switchbrew does not match with HW
- NfcXcdHandle nfc_states;
- INSERT_PADDING_BYTES(0x8); // Mutex
- TriggerGeneric gc_trigger_states;
- INSERT_PADDING_BYTES(0xc1f);
- };
- static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
-
- struct ControllerHolder {
- NPadControllerType type;
- bool is_connected;
- };
-
- void InitNewlyAddedController(std::size_t controller_idx);
- bool IsControllerSupported(NPadControllerType controller) const;
- void RequestPadStateUpdate(u32 npad_id);
-
- std::atomic<u32> press_state{};
-
- NpadStyleSet style{};
- std::array<NPadEntry, 10> shared_memory_entries{};
- using ButtonArray = std::array<
- std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
- 10>;
- using StickArray = std::array<
- std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
- 10>;
- using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
- Settings::NativeVibration::NUM_VIBRATIONS_HID>,
- 10>;
- using MotionArray = std::array<
- std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
- 10>;
-
+ NpadSystemButtonProperties button_properties;
+ Core::HID::NpadBatteryLevel battery_level_dual;
+ Core::HID::NpadBatteryLevel battery_level_left;
+ Core::HID::NpadBatteryLevel battery_level_right;
+ union {
+ Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
+ AppletFooterUi applet_footer;
+ };
+ INSERT_PADDING_BYTES(0x20); // Unknown
+ Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
+ NpadLarkType lark_type_l_and_main;
+ NpadLarkType lark_type_r;
+ NpadLuciaType lucia_type;
+ NpadLagonType lagon_type;
+ NpadLagerType lager_type;
+ // FW 13.x Investigate there is some sort of bitflag related to joycons
+ INSERT_PADDING_BYTES(0x4);
+ INSERT_PADDING_BYTES(0xc08); // Unknown
+ };
+ static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
+
+ struct VibrationData {
+ bool device_mounted{};
+ Core::HID::VibrationValue latest_vibration_value{};
+ std::chrono::steady_clock::time_point last_vibration_timepoint{};
+ };
+
+ struct NpadControllerData {
+ Core::HID::EmulatedController* device;
+ Kernel::KEvent* styleset_changed_event{};
+ NpadInternalState shared_memory_entry{};
+
+ std::array<VibrationData, 2> vibration{};
+ bool unintended_home_button_input_protection{};
+ bool is_connected{};
+
+ // Dual joycons can have only one side connected
+ bool is_dual_left_connected{true};
+ bool is_dual_right_connected{true};
+
+ // Motion parameters
+ bool sixaxis_at_rest{true};
+ bool sixaxis_sensor_enabled{true};
+ bool sixaxis_fusion_enabled{false};
+ Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{};
+ GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
+
+ // Current pad state
+ NPadGenericState npad_pad_state{};
+ NPadGenericState npad_libnx_state{};
+ NpadGcTriggerState npad_trigger_state{};
+ SixAxisSensorState sixaxis_fullkey_state{};
+ SixAxisSensorState sixaxis_handheld_state{};
+ SixAxisSensorState sixaxis_dual_left_state{};
+ SixAxisSensorState sixaxis_dual_right_state{};
+ SixAxisSensorState sixaxis_left_lifo_state{};
+ SixAxisSensorState sixaxis_right_lifo_state{};
+ int callback_key;
+ };
+
+ void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
+ void InitNewlyAddedController(Core::HID::NpadIdType npad_id);
+ bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;
+ void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
+ void WriteEmptyEntry(NpadInternalState& npad);
+
+ NpadControllerData& GetControllerFromHandle(
+ const Core::HID::SixAxisSensorHandle& device_handle);
+ const NpadControllerData& GetControllerFromHandle(
+ const Core::HID::SixAxisSensorHandle& device_handle) const;
+ NpadControllerData& GetControllerFromHandle(
+ const Core::HID::VibrationDeviceHandle& device_handle);
+ const NpadControllerData& GetControllerFromHandle(
+ const Core::HID::VibrationDeviceHandle& device_handle) const;
+ NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
+ const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
+
+ std::atomic<u64> press_state{};
+
+ std::array<NpadControllerData, 10> controller_data{};
KernelHelpers::ServiceContext& service_context;
std::mutex mutex;
- ButtonArray buttons;
- StickArray sticks;
- VibrationArray vibrations;
- MotionArray motions;
- std::vector<u32> supported_npad_id_types{};
- NpadHoldType hold_type{NpadHoldType::Vertical};
+ std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
+ NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical};
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
- // Each controller should have their own styleset changed event
- std::array<Kernel::KEvent*, 10> styleset_changed_events{};
- std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10>
- last_vibration_timepoints{};
- std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
bool permit_vibration_session_enabled{false};
- std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
- std::array<ControllerHolder, 10> connected_controllers{};
- std::array<bool, 10> unintended_home_button_input_protection{};
bool analog_stick_use_center_clamp{};
- GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
- bool sixaxis_sensors_enabled{true};
- f32 sixaxis_fusion_parameter1{};
- f32 sixaxis_fusion_parameter2{};
- bool sixaxis_at_rest{true};
- std::array<ControllerPad, 10> npad_pad_states{};
- std::array<TriggerState, 10> npad_trigger_states{};
bool is_in_lr_assignment_mode{false};
};
} // 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 @@
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
+#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/stubbed.h"
namespace Service::HID {
-Controller_Stubbed::Controller_Stubbed(Core::System& system_) : ControllerBase{system_} {}
+Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
@@ -31,10 +32,9 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
}
-void Controller_Stubbed::OnLoadInputDevices() {}
-
void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
common_offset = off;
smart_update = true;
}
+
} // 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 @@
namespace Service::HID {
class Controller_Stubbed final : public ControllerBase {
public:
- explicit Controller_Stubbed(Core::System& system_);
+ explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_);
~Controller_Stubbed() override;
// Called when the controller is initialized
@@ -22,12 +22,17 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
void SetCommonHeaderOffset(std::size_t off);
private:
+ struct CommonHeader {
+ s64 timestamp;
+ s64 total_entry_count;
+ s64 last_entry_index;
+ s64 entry_count;
+ };
+ static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
+
bool smart_update{};
std::size_t common_offset{};
};
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 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
+#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
-#include "core/frontend/input.h"
+#include "core/hid/emulated_console.h"
+#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
-Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {}
+Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_)
+ : ControllerBase{hid_core_} {
+ console = hid_core.GetEmulatedConsole();
+}
+
Controller_Touchscreen::~Controller_Touchscreen() = default;
-void Controller_Touchscreen::OnInit() {
- for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
- mouse_finger_id[id] = MAX_FINGERS;
- keyboard_finger_id[id] = MAX_FINGERS;
- udp_finger_id[id] = MAX_FINGERS;
- }
-}
+void Controller_Touchscreen::OnInit() {}
void Controller_Touchscreen::OnRelease() {}
void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- shared_memory.header.timestamp = core_timing.GetCPUTicks();
- shared_memory.header.total_entry_count = 17;
+ touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
if (!IsControllerActivated()) {
- shared_memory.header.entry_count = 0;
- shared_memory.header.last_entry_index = 0;
+ touch_screen_lifo.buffer_count = 0;
+ touch_screen_lifo.buffer_tail = 0;
+ std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo));
return;
}
- shared_memory.header.entry_count = 16;
- const auto& last_entry =
- shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
- shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
- auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
+ const auto touch_status = console->GetTouch();
+ for (std::size_t id = 0; id < MAX_FINGERS; id++) {
+ const auto& current_touch = touch_status[id];
+ auto& finger = fingers[id];
+ finger.position = current_touch.position;
+ finger.id = current_touch.id;
- cur_entry.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number2 = cur_entry.sampling_number;
+ if (finger.attribute.start_touch) {
+ finger.attribute.raw = 0;
+ continue;
+ }
- const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
- const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
- for (std::size_t id = 0; id < mouse_status.size(); ++id) {
- mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
- udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
- }
+ if (finger.attribute.end_touch) {
+ finger.attribute.raw = 0;
+ finger.pressed = false;
+ continue;
+ }
+
+ if (!finger.pressed && current_touch.pressed) {
+ finger.attribute.start_touch.Assign(1);
+ finger.pressed = true;
+ continue;
+ }
- if (Settings::values.use_touch_from_button) {
- const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
- for (std::size_t id = 0; id < mouse_status.size(); ++id) {
- keyboard_finger_id[id] =
- UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
+ if (finger.pressed && !current_touch.pressed) {
+ finger.attribute.raw = 0;
+ finger.attribute.end_touch.Assign(1);
}
}
- std::array<Finger, 16> active_fingers;
+ std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
const auto active_fingers_count =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
const u64 tick = core_timing.GetCPUTicks();
- cur_entry.entry_count = static_cast<s32_le>(active_fingers_count);
+ const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state;
+
+ next_state.sampling_number = last_entry.sampling_number + 1;
+ next_state.entry_count = static_cast<s32>(active_fingers_count);
+
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
- auto& touch_entry = cur_entry.states[id];
+ auto& touch_entry = next_state.states[id];
if (id < active_fingers_count) {
const auto& [active_x, active_y] = active_fingers[id].position;
touch_entry.position = {
@@ -97,66 +107,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
touch_entry.finger = 0;
}
}
- std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
-}
-
-void Controller_Touchscreen::OnLoadInputDevices() {
- touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
- touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
- touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
-}
-
-std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
- // Dont assign any touch input to a finger if disabled
- if (!Settings::values.touchscreen.enabled) {
- return std::nullopt;
- }
- std::size_t first_free_id = 0;
- while (first_free_id < MAX_FINGERS) {
- if (!fingers[first_free_id].pressed) {
- return first_free_id;
- } else {
- first_free_id++;
- }
- }
- return std::nullopt;
-}
-
-std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
- const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
- const auto& [x, y, pressed] = touch_input;
- if (finger_id > MAX_FINGERS) {
- LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
- return MAX_FINGERS;
- }
- if (pressed) {
- Attributes attribute{};
- if (finger_id == MAX_FINGERS) {
- const auto first_free_id = GetUnusedFingerID();
- if (!first_free_id) {
- // Invalid finger id do nothing
- return MAX_FINGERS;
- }
- finger_id = first_free_id.value();
- fingers[finger_id].pressed = true;
- fingers[finger_id].id = static_cast<u32_le>(finger_id);
- attribute.start_touch.Assign(1);
- }
- fingers[finger_id].position = {x, y};
- fingers[finger_id].attribute = attribute;
- return finger_id;
- }
-
- if (finger_id != MAX_FINGERS) {
- if (!fingers[finger_id].attribute.end_touch) {
- fingers[finger_id].attribute.end_touch.Assign(1);
- fingers[finger_id].attribute.start_touch.Assign(0);
- return finger_id;
- }
- fingers[finger_id].pressed = false;
- }
- return MAX_FINGERS;
+ touch_screen_lifo.WriteNextEntry(next_state);
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo));
}
} // 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 @@
#include "common/common_types.h"
#include "common/point.h"
#include "common/swap.h"
-#include "core/frontend/input.h"
+#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/ring_lifo.h"
+
+namespace Core::HID {
+class EmulatedConsole;
+} // namespace Core::HID
namespace Service::HID {
class Controller_Touchscreen final : public ControllerBase {
public:
+ // This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
+ // This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode;
INSERT_PADDING_BYTES_NOINIT(0x7);
@@ -29,7 +36,7 @@ public:
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
"TouchScreenConfigurationForNx is an invalid size");
- explicit Controller_Touchscreen(Core::System& system_);
+ explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_);
~Controller_Touchscreen() override;
// Called when the controller is initialized
@@ -41,73 +48,24 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
private:
static constexpr std::size_t MAX_FINGERS = 16;
- // Returns an unused finger id, if there is no fingers available std::nullopt will be returned
- std::optional<std::size_t> GetUnusedFingerID() const;
-
- // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
- // changes will be made. Updates the coordinates if the finger id it's already set. If the touch
- // ends delays the output by one frame to set the end_touch flag before finally freeing the
- // finger id
- std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
- std::size_t finger_id);
-
- struct Attributes {
- union {
- u32 raw{};
- BitField<0, 1, u32> start_touch;
- BitField<1, 1, u32> end_touch;
- };
- };
- static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
-
- struct TouchState {
- u64_le delta_time;
- Attributes attribute;
- u32_le finger;
- Common::Point<u32_le> position;
- u32_le diameter_x;
- u32_le diameter_y;
- u32_le rotation_angle;
+ // This is nn::hid::TouchScreenState
+ struct TouchScreenState {
+ s64 sampling_number;
+ s32 entry_count;
+ INSERT_PADDING_BYTES(4); // Reserved
+ std::array<Core::HID::TouchState, MAX_FINGERS> states;
};
- static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
+ static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
- struct TouchScreenEntry {
- s64_le sampling_number;
- s64_le sampling_number2;
- s32_le entry_count;
- std::array<TouchState, MAX_FINGERS> states;
- };
- static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
-
- struct TouchScreenSharedMemory {
- CommonHeader header;
- std::array<TouchScreenEntry, 17> shared_memory_entries{};
- INSERT_PADDING_BYTES(0x3c8);
- };
- static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
- "TouchScreenSharedMemory is an invalid size");
-
- struct Finger {
- u64_le last_touch{};
- Common::Point<float> position;
- u32_le id{};
- bool pressed{};
- Attributes attribute;
- };
+ // This is nn::hid::detail::TouchScreenLifo
+ Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
+ static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
+ TouchScreenState next_state{};
- TouchScreenSharedMemory shared_memory{};
- std::unique_ptr<Input::TouchDevice> touch_mouse_device;
- std::unique_ptr<Input::TouchDevice> touch_udp_device;
- std::unique_ptr<Input::TouchDevice> touch_btn_device;
- std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
- std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
- std::array<std::size_t, MAX_FINGERS> udp_finger_id;
- std::array<Finger, MAX_FINGERS> fingers;
+ std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers;
+ Core::HID::EmulatedConsole* console;
};
} // 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 @@
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
+#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
-Controller_XPad::Controller_XPad(Core::System& system_) : ControllerBase{system_} {}
+Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}
@@ -19,28 +20,19 @@ void Controller_XPad::OnRelease() {}
void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {
- for (auto& xpad_entry : shared_memory.shared_memory_entries) {
- xpad_entry.header.timestamp = core_timing.GetCPUTicks();
- xpad_entry.header.total_entry_count = 17;
-
- if (!IsControllerActivated()) {
- xpad_entry.header.entry_count = 0;
- xpad_entry.header.last_entry_index = 0;
- return;
- }
- xpad_entry.header.entry_count = 16;
-
- const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
- xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
- auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
-
- cur_entry.sampling_number = last_entry.sampling_number + 1;
- cur_entry.sampling_number2 = cur_entry.sampling_number;
+ if (!IsControllerActivated()) {
+ basic_xpad_lifo.buffer_count = 0;
+ basic_xpad_lifo.buffer_tail = 0;
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
+ return;
}
+
+ const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state;
+ next_state.sampling_number = last_entry.sampling_number + 1;
// TODO(ogniK): Update xpad states
- std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
+ basic_xpad_lifo.WriteNextEntry(next_state);
+ std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
}
-void Controller_XPad::OnLoadInputDevices() {}
} // 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 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class Controller_XPad final : public ControllerBase {
public:
- explicit Controller_XPad(Core::System& system_);
+ explicit Controller_XPad(Core::HID::HIDCore& hid_core_);
~Controller_XPad() override;
// Called when the controller is initialized
@@ -25,13 +27,11 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
- // Called when input devices should be loaded
- void OnLoadInputDevices() override;
-
private:
- struct Attributes {
+ // This is nn::hid::BasicXpadAttributeSet
+ struct BasicXpadAttributeSet {
union {
- u32_le raw{};
+ u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
@@ -40,11 +40,12 @@ private:
BitField<5, 1, u32> is_right_wired;
};
};
- static_assert(sizeof(Attributes) == 4, "Attributes is an invalid size");
+ static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
- struct Buttons {
+ // This is nn::hid::BasicXpadButtonSet
+ struct BasicXpadButtonSet {
union {
- u32_le raw{};
+ u32 raw{};
// Button states
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
@@ -88,35 +89,21 @@ private:
BitField<30, 1, u32> handheld_left_b;
};
};
- static_assert(sizeof(Buttons) == 4, "Buttons is an invalid size");
-
- struct AnalogStick {
- s32_le x;
- s32_le y;
- };
- static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size");
-
- struct XPadState {
- s64_le sampling_number;
- s64_le sampling_number2;
- Attributes attributes;
- Buttons pad_states;
- AnalogStick l_stick;
- AnalogStick r_stick;
+ static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
+
+ // This is nn::hid::detail::BasicXpadState
+ struct BasicXpadState {
+ s64 sampling_number;
+ BasicXpadAttributeSet attributes;
+ BasicXpadButtonSet pad_states;
+ Core::HID::AnalogStickState l_stick;
+ Core::HID::AnalogStickState r_stick;
};
- static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
+ static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
- struct XPadEntry {
- CommonHeader header;
- std::array<XPadState, 17> pad_states{};
- INSERT_PADDING_BYTES(0x138);
- };
- static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
-
- struct SharedMemory {
- std::array<XPadEntry, 4> shared_memory_entries{};
- };
- static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
- SharedMemory shared_memory{};
+ // This is nn::hid::detail::BasicXpadLifo
+ Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
+ static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
+ BasicXpadState next_state{};
};
} // 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 @@
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
-#include "core/frontend/input.h"
+#include "core/hid/hid_core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h"
@@ -34,10 +34,10 @@
namespace Service::HID {
// Updating period for each HID device.
-// HID is polled every 15ms, this value was derived from
-// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
-constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz)
-constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
+// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
+constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
+constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
+constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system_,
@@ -79,17 +79,24 @@ IAppletResource::IAppletResource(Core::System& system_,
const auto guard = LockService();
UpdateControllers(user_data, ns_late);
});
+ mouse_keyboard_update_event = Core::Timing::CreateEvent(
+ "HID::UpdateMouseKeyboardCallback",
+ [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ const auto guard = LockService();
+ UpdateMouseKeyboard(user_data, ns_late);
+ });
motion_update_event = Core::Timing::CreateEvent(
- "HID::MotionPadCallback",
+ "HID::UpdateMotionCallback",
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
const auto guard = LockService();
UpdateMotion(user_data, ns_late);
});
system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
+ system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event);
system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
- ReloadInputDevices();
+ system.HIDCore().ReloadInputDevices();
}
void IAppletResource::ActivateController(HidController controller) {
@@ -102,6 +109,7 @@ void IAppletResource::DeactivateController(HidController controller) {
IAppletResource::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
+ system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
}
@@ -117,23 +125,44 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
- const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
for (const auto& controller : controllers) {
- if (should_reload) {
- controller->OnLoadInputDevices();
+ // Keyboard has it's own update event
+ if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
+ continue;
+ }
+ // Mouse has it's own update event
+ if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
+ continue;
}
controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(),
SHARED_MEMORY_SIZE);
}
// If ns_late is higher than the update rate ignore the delay
- if (ns_late > motion_update_ns) {
+ if (ns_late > pad_update_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
}
+void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
+ std::chrono::nanoseconds ns_late) {
+ auto& core_timing = system.CoreTiming();
+
+ controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(
+ core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
+ controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(
+ core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
+
+ // If ns_late is higher than the update rate ignore the delay
+ if (ns_late > mouse_keyboard_update_ns) {
+ ns_late = {};
+ }
+
+ core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
+}
+
void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
@@ -166,7 +195,7 @@ public:
private:
void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+ const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
if (applet_resource != nullptr) {
applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -264,8 +293,8 @@ Hid::Hid(Core::System& system_)
{132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
{134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
- {135, nullptr, "SetNpadCaptureButtonAssignment"},
- {136, nullptr, "ClearNpadCaptureButtonAssignment"},
+ {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
+ {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -422,6 +451,7 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -448,19 +478,18 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ u32 basic_xpad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
+ // This function does nothing on 10.0.0+
- LOG_DEBUG(Service_HID,
- "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
- parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
- parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+ LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
+ parameters.basic_xpad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -469,19 +498,18 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ u32 basic_xpad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
+ // This function does nothing on 10.0.0+
- LOG_DEBUG(Service_HID,
- "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
- parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
- parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+ LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
+ parameters.basic_xpad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -490,14 +518,16 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSixAxisEnabled(parameters.sixaxis_handle, true);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -511,14 +541,16 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSixAxisEnabled(parameters.sixaxis_handle, false);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -534,19 +566,23 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
struct Parameters {
bool enable_sixaxis_sensor_fusion;
INSERT_PADDING_BYTES_NOINIT(3);
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- LOG_WARNING(Service_HID,
- "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
- "device_index={}, applet_resource_user_id={}",
- parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
- parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
- parameters.applet_resource_user_id);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSixAxisFusionEnabled(parameters.sixaxis_handle,
+ parameters.enable_sixaxis_sensor_fusion);
+
+ LOG_DEBUG(Service_HID,
+ "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
+ "device_index={}, applet_resource_user_id={}",
+ parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
+ parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -555,9 +591,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
- f32 parameter1;
- f32 parameter2;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
+ INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
@@ -565,14 +601,14 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2);
+ .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
- LOG_WARNING(Service_HID,
- "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
- "parameter2={}, applet_resource_user_id={}",
- parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
- parameters.sixaxis_handle.device_index, parameters.parameter1,
- parameters.parameter2, parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
+ "parameter2={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
+ parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -581,35 +617,33 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
- f32 parameter1 = 0;
- f32 parameter2 = 0;
const auto parameters{rp.PopRaw<Parameters>()};
- std::tie(parameter1, parameter2) =
+ const auto sixaxis_fusion_parameters =
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetSixAxisFusionParameters();
+ .GetSixAxisFusionParameters(parameters.sixaxis_handle);
- LOG_WARNING(
- Service_HID,
- "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
- parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
- parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
- rb.Push(parameter1);
- rb.Push(parameter2);
+ rb.PushRaw(sixaxis_fusion_parameters);
}
void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -617,13 +651,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .ResetSixAxisFusionParameters();
+ .ResetSixAxisFusionParameters(parameters.sixaxis_handle);
- LOG_WARNING(
- Service_HID,
- "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
- parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
- parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -631,12 +664,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+ const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetGyroscopeZeroDriftMode(drift_mode);
+ .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -651,10 +684,11 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -666,21 +700,23 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetGyroscopeZeroDriftMode());
+ .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
}
void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
+ const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard);
+ .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -694,10 +730,11 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -709,16 +746,17 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .IsSixAxisSensorAtRest());
+ .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
}
void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -740,13 +778,14 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->ActivateController(HidController::Gesture);
- LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
- parameters.applet_resource_user_id);
+ LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
+ parameters.unknown, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -754,12 +793,20 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto supported_styleset{rp.Pop<u32>()};
+ struct Parameters {
+ Core::HID::NpadStyleSet supported_styleset;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSupportedStyleSet({supported_styleset});
+ .SetSupportedStyleSet({parameters.supported_styleset});
- LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
+ LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
+ parameters.supported_styleset, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -773,9 +820,9 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetSupportedStyleSet()
- .raw);
+ rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetSupportedStyleSet()
+ .raw);
}
void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
@@ -818,11 +865,12 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id;
+ Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
u64 unknown;
};
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -838,10 +886,11 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id;
+ Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -857,7 +906,7 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
+ const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
@@ -872,16 +921,17 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
// Should have no effect with how our npad sets up the data
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 unknown;
+ s32 revision;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->ActivateController(HidController::NPad);
- LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
+ LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -891,7 +941,7 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()};
+ const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
@@ -916,42 +966,44 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id;
+ Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
- parameters.npad_id, parameters.applet_resource_user_id);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
- // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id;
+ Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- u64 npad_joy_device_type;
+ Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
};
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
+ .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
- LOG_WARNING(Service_HID,
- "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
- parameters.npad_id, parameters.applet_resource_user_id,
- parameters.npad_joy_device_type);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
+ parameters.npad_id, parameters.applet_resource_user_id,
+ parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -960,18 +1012,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id;
+ Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual);
+ .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
- parameters.npad_id, parameters.applet_resource_user_id);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -979,8 +1032,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id_1{rp.Pop<u32>()};
- const auto npad_id_2{rp.Pop<u32>()};
+ const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
+ const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1046,8 +1099,8 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id_1{rp.Pop<u32>()};
- const auto npad_id_2{rp.Pop<u32>()};
+ const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
+ const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1068,10 +1121,11 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- u32 npad_id;
+ Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1089,9 +1143,10 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
struct Parameters {
bool unintended_home_button_input_protection;
INSERT_PADDING_BYTES_NOINIT(3);
- u32 npad_id;
+ Core::HID::NpadIdType npad_id;
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1113,6 +1168,7 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool analog_stick_use_center_clamp;
+ INSERT_PADDING_BYTES_NOINIT(7);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -1130,40 +1186,71 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
+void Hid::SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::NpadStyleSet npad_styleset;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ Core::HID::NpadButton button;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
+ parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+ const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
- VibrationDeviceInfo vibration_device_info;
+ Core::HID::VibrationDeviceInfo vibration_device_info;
switch (vibration_device_handle.npad_type) {
- case Controller_NPad::NpadType::ProController:
- case Controller_NPad::NpadType::Handheld:
- case Controller_NPad::NpadType::JoyconDual:
- case Controller_NPad::NpadType::JoyconLeft:
- case Controller_NPad::NpadType::JoyconRight:
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Handheld:
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ case Core::HID::NpadStyleIndex::JoyconRight:
default:
- vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
+ vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
break;
- case Controller_NPad::NpadType::GameCube:
- vibration_device_info.type = VibrationDeviceType::GcErm;
+ case Core::HID::NpadStyleIndex::GameCube:
+ vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
break;
- case Controller_NPad::NpadType::Pokeball:
- vibration_device_info.type = VibrationDeviceType::Unknown;
+ case Core::HID::NpadStyleIndex::Pokeball:
+ vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
break;
}
switch (vibration_device_handle.device_index) {
- case Controller_NPad::DeviceIndex::Left:
- vibration_device_info.position = VibrationDevicePosition::Left;
+ case Core::HID::DeviceIndex::Left:
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
break;
- case Controller_NPad::DeviceIndex::Right:
- vibration_device_info.position = VibrationDevicePosition::Right;
+ case Core::HID::DeviceIndex::Right:
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
break;
- case Controller_NPad::DeviceIndex::None:
+ case Core::HID::DeviceIndex::None:
default:
UNREACHABLE_MSG("DeviceIndex should never be None!");
- vibration_device_info.position = VibrationDevicePosition::None;
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
break;
}
@@ -1178,11 +1265,12 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle vibration_device_handle;
- Controller_NPad::VibrationValue vibration_value;
+ Core::HID::VibrationDeviceHandle vibration_device_handle;
+ Core::HID::VibrationValue vibration_value;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1202,10 +1290,11 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle vibration_device_handle;
+ Core::HID::VibrationDeviceHandle vibration_device_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1256,10 +1345,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
const auto handles = ctx.ReadBuffer(0);
const auto vibrations = ctx.ReadBuffer(1);
- std::vector<Controller_NPad::DeviceHandle> vibration_device_handles(
- handles.size() / sizeof(Controller_NPad::DeviceHandle));
- std::vector<Controller_NPad::VibrationValue> vibration_values(
- vibrations.size() / sizeof(Controller_NPad::VibrationValue));
+ std::vector<Core::HID::VibrationDeviceHandle> vibration_device_handles(
+ handles.size() / sizeof(Core::HID::VibrationDeviceHandle));
+ std::vector<Core::HID::VibrationValue> vibration_values(vibrations.size() /
+ sizeof(Core::HID::VibrationValue));
std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
@@ -1276,9 +1365,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle vibration_device_handle;
+ Core::HID::VibrationDeviceHandle vibration_device_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- VibrationGcErmCommand gc_erm_command;
+ Core::HID::VibrationGcErmCommand gc_erm_command;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
@@ -1292,26 +1382,26 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
*/
const auto vibration_value = [parameters] {
switch (parameters.gc_erm_command) {
- case VibrationGcErmCommand::Stop:
- return Controller_NPad::VibrationValue{
- .amp_low = 0.0f,
- .freq_low = 160.0f,
- .amp_high = 0.0f,
- .freq_high = 320.0f,
+ case Core::HID::VibrationGcErmCommand::Stop:
+ return Core::HID::VibrationValue{
+ .low_amplitude = 0.0f,
+ .low_frequency = 160.0f,
+ .high_amplitude = 0.0f,
+ .high_frequency = 320.0f,
};
- case VibrationGcErmCommand::Start:
- return Controller_NPad::VibrationValue{
- .amp_low = 1.0f,
- .freq_low = 160.0f,
- .amp_high = 1.0f,
- .freq_high = 320.0f,
+ case Core::HID::VibrationGcErmCommand::Start:
+ return Core::HID::VibrationValue{
+ .low_amplitude = 1.0f,
+ .low_frequency = 160.0f,
+ .high_amplitude = 1.0f,
+ .high_frequency = 320.0f,
};
- case VibrationGcErmCommand::StopHard:
- return Controller_NPad::VibrationValue{
- .amp_low = 0.0f,
- .freq_low = 0.0f,
- .amp_high = 0.0f,
- .freq_high = 0.0f,
+ case Core::HID::VibrationGcErmCommand::StopHard:
+ return Core::HID::VibrationValue{
+ .low_amplitude = 0.0f,
+ .low_frequency = 0.0f,
+ .high_amplitude = 0.0f,
+ .high_frequency = 0.0f,
};
default:
return Controller_NPad::DEFAULT_VIBRATION_VALUE;
@@ -1336,7 +1426,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle vibration_device_handle;
+ Core::HID::VibrationDeviceHandle vibration_device_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -1347,8 +1437,8 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
.GetLastVibration(parameters.vibration_device_handle);
const auto gc_erm_command = [last_vibration] {
- if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) {
- return VibrationGcErmCommand::Start;
+ if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
+ return Core::HID::VibrationGcErmCommand::Start;
}
/**
@@ -1357,11 +1447,11 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
* SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
* This is done to reuse the controller vibration functions made for regular controllers.
*/
- if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) {
- return VibrationGcErmCommand::StopHard;
+ if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
+ return Core::HID::VibrationGcErmCommand::StopHard;
}
- return VibrationGcErmCommand::Stop;
+ return Core::HID::VibrationGcErmCommand::Stop;
}();
LOG_DEBUG(Service_HID,
@@ -1401,10 +1491,11 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle vibration_device_handle;
+ Core::HID::VibrationDeviceHandle vibration_device_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -1435,18 +1526,18 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- LOG_WARNING(
- Service_HID,
- "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
- parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
- parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
+ parameters.console_sixaxis_handle.unknown_1,
+ parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1455,18 +1546,18 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Controller_NPad::DeviceHandle sixaxis_handle;
+ Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- LOG_WARNING(
- Service_HID,
- "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
- parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
- parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
+ parameters.console_sixaxis_handle.unknown_1,
+ parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1620,10 +1711,8 @@ void Hid::SetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
- applet_resource_user_id);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -1825,7 +1914,7 @@ public:
{317, nullptr, "GetNpadLeftRightInterfaceType"},
{318, nullptr, "HasBattery"},
{319, nullptr, "HasLeftRightBattery"},
- {321, nullptr, "GetUniquePadsFromNpad"},
+ {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
{322, nullptr, "GetIrSensorState"},
{323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
{324, nullptr, "GetUniquePadButtonSet"},
@@ -1996,6 +2085,18 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
+
+ void GetUniquePadsFromNpad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
+
+ const s64 total_entries = 0;
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(total_entries);
+ }
};
class HidTmp final : public ServiceFramework<HidTmp> {
@@ -2037,10 +2138,6 @@ public:
}
};
-void ReloadInputDevices() {
- Settings::values.is_device_reload_pending.store(true);
-}
-
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<Hid>(system)->InstallAsService(service_manager);
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:
private:
template <typename T>
void MakeController(HidController controller) {
- controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system);
+ controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore());
}
template <typename T>
void MakeControllerWithServiceContext(HidController controller) {
controllers[static_cast<std::size_t>(controller)] =
- std::make_unique<T>(system, service_context);
+ std::make_unique<T>(system.HIDCore(), service_context);
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
KernelHelpers::ServiceContext& service_context;
std::shared_ptr<Core::Timing::EventType> pad_update_event;
+ std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -134,6 +136,8 @@ private:
void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx);
+ void SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
+ void ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
@@ -161,38 +165,11 @@ private:
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
- enum class VibrationDeviceType : u32 {
- Unknown = 0,
- LinearResonantActuator = 1,
- GcErm = 2,
- };
-
- enum class VibrationDevicePosition : u32 {
- None = 0,
- Left = 1,
- Right = 2,
- };
-
- enum class VibrationGcErmCommand : u64 {
- Stop = 0,
- Start = 1,
- StopHard = 2,
- };
-
- struct VibrationDeviceInfo {
- VibrationDeviceType type{};
- VibrationDevicePosition position{};
- };
- static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
-
std::shared_ptr<IAppletResource> applet_resource;
KernelHelpers::ServiceContext service_context;
};
-/// Reload input devices. Used when input configuration changed
-void ReloadInputDevices();
-
/// Registers all HID services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+
+namespace Service::HID {
+
+template <typename State>
+struct AtomicStorage {
+ s64 sampling_number;
+ State state;
+};
+
+template <typename State, std::size_t max_buffer_size>
+struct Lifo {
+ s64 timestamp{};
+ s64 total_buffer_count = static_cast<s64>(max_buffer_size);
+ s64 buffer_tail{};
+ s64 buffer_count{};
+ std::array<AtomicStorage<State>, max_buffer_size> entries{};
+
+ const AtomicStorage<State>& ReadCurrentEntry() const {
+ return entries[buffer_tail];
+ }
+
+ const AtomicStorage<State>& ReadPreviousEntry() const {
+ return entries[GetPreviousEntryIndex()];
+ }
+
+ std::size_t GetPreviousEntryIndex() const {
+ return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count);
+ }
+
+ std::size_t GetNextEntryIndex() const {
+ return static_cast<size_t>((buffer_tail + 1) % total_buffer_count);
+ }
+
+ void WriteNextEntry(const State& new_state) {
+ if (buffer_count < total_buffer_count - 1) {
+ buffer_count++;
+ }
+ buffer_tail = GetNextEntryIndex();
+ const auto& previous_entry = ReadPreviousEntry();
+ entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1;
+ entries[buffer_tail].state = new_state;
+ }
+};
+
+} // 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:
CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
nro_header.segment_headers[DATA_INDEX].memory_size);
- CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
+ CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute));
- CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(ro_start, data_start - ro_start,
- Kernel::KMemoryPermission::Read));
+ CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
+ ro_start, data_start - ro_start, Kernel::KMemoryPermission::Read));
- return process->PageTable().SetCodeMemoryPermission(
+ return process->PageTable().SetProcessMemoryPermission(
data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite);
}
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 @@
#include "core/hle/service/ns/errors.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns.h"
+#include "core/hle/service/ns/pdm_qry.h"
#include "core/hle/service/ns/pl_u.h"
#include "core/hle/service/set/set.h"
@@ -570,11 +571,29 @@ IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
IFactoryResetInterface::~IFactoryResetInterface() = default;
+IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
+ Core::System& system_)
+ : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetApplicationControlData"},
+ {1, nullptr, "GetApplicationDesiredLanguage"},
+ {2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
+ {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
+ {4, nullptr, "SelectApplicationDesiredLanguage"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
+
NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
// clang-format off
static const FunctionInfo functions[] = {
{7988, nullptr, "GetDynamicRightsInterface"},
- {7989, nullptr, "GetReadOnlyApplicationControlDataInterface"},
+ {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
{7991, nullptr, "GetReadOnlyApplicationRecordInterface"},
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
@@ -738,6 +757,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<NS_SU>(system)->InstallAsService(service_manager);
std::make_shared<NS_VM>(system)->InstallAsService(service_manager);
+ std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager);
+
std::make_shared<PL_U>(system)->InstallAsService(service_manager);
}
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:
~IFactoryResetInterface() override;
};
+class IReadOnlyApplicationControlDataInterface final
+ : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
+public:
+ explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
+ ~IReadOnlyApplicationControlDataInterface() override;
+};
+
class NS final : public ServiceFramework<NS> {
public:
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include <memory>
+
+#include "common/logging/log.h"
+#include "common/uuid.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/ns/pdm_qry.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::NS {
+
+PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "QueryAppletEvent"},
+ {1, nullptr, "QueryPlayStatistics"},
+ {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
+ {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
+ {4, nullptr, "QueryPlayStatisticsByApplicationId"},
+ {5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
+ {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
+ {7, nullptr, "QueryLastPlayTimeV0"},
+ {8, nullptr, "QueryPlayEvent"},
+ {9, nullptr, "GetAvailablePlayEventRange"},
+ {10, nullptr, "QueryAccountEvent"},
+ {11, nullptr, "QueryAccountPlayEvent"},
+ {12, nullptr, "GetAvailableAccountPlayEventRange"},
+ {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
+ {14, nullptr, "QueryRecentlyPlayedApplication"},
+ {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
+ {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
+ {17, nullptr, "QueryLastPlayTime"},
+ {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
+ {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+PDM_QRY::~PDM_QRY() = default;
+
+void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto unknown = rp.Pop<bool>();
+ rp.Pop<u8>(); // Padding
+ const auto application_id = rp.Pop<u64>();
+ const auto user_account_uid = rp.PopRaw<Common::UUID>();
+
+ // TODO(German77): Read statistics of the game
+ PlayStatistics statistics{
+ .application_id = application_id,
+ .total_launches = 1,
+ };
+
+ LOG_WARNING(Service_NS,
+ "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
+ unknown, application_id, user_account_uid.Format());
+
+ IPC::ResponseBuilder rb{ctx, 12};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(statistics);
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NS {
+
+struct PlayStatistics {
+ u64 application_id{};
+ u32 first_entry_index{};
+ u32 first_timestamp_user{};
+ u32 first_timestamp_network{};
+ u32 last_entry_index{};
+ u32 last_timestamp_user{};
+ u32 last_timestamp_network{};
+ u32 play_time_in_minutes{};
+ u32 total_launches{};
+};
+static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
+
+class PDM_QRY final : public ServiceFramework<PDM_QRY> {
+public:
+ explicit PDM_QRY(Core::System& system_);
+ ~PDM_QRY() override;
+
+private:
+ void QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx);
+};
+
+} // 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>&
switch (command.group) {
case 0x0:
switch (command.cmd) {
- case 0x1:
- return Submit(input, output);
+ case 0x1: {
+ if (!fd_to_id.contains(fd)) {
+ fd_to_id[fd] = next_id++;
+ }
+ return Submit(fd, input, output);
+ }
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -66,7 +70,10 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {}
void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
- system.GPU().ClearCdmaInstance();
+ const auto iter = fd_to_id.find(fd);
+ if (iter != fd_to_id.end()) {
+ system.GPU().ClearCdmaInstance(iter->second);
+ }
}
} // 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:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+
+private:
+ u32 next_id{};
};
} // 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) {
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
+ std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
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
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
- gpu.PushCommandBuffer(cmdlist);
+ gpu.PushCommandBuffer(fd_to_id[fd], cmdlist);
}
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
// 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:
/// Ioctl command implementations
NvResult SetNVMAPfd(const std::vector<u8>& input);
- NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+ std::unordered_map<DeviceFD, u32> fd_to_id{};
s32_le nvmap_fd{};
u32_le submit_timeout{};
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
case 0x0:
switch (command.cmd) {
case 0x1:
- return Submit(input, output);
+ if (!fd_to_id.contains(fd)) {
+ fd_to_id[fd] = next_id++;
+ }
+ return Submit(fd, input, output);
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -65,7 +68,10 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
void nvhost_vic::OnOpen(DeviceFD fd) {}
void nvhost_vic::OnClose(DeviceFD fd) {
- system.GPU().ClearCdmaInstance();
+ const auto iter = fd_to_id.find(fd);
+ if (iter != fd_to_id.end()) {
+ system.GPU().ClearCdmaInstance(iter->second);
+ }
}
} // 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:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+
+private:
+ u32 next_id{};
};
} // 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 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
#pragma once
#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 {
namespace {
-constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1};
+constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1};
+[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2};
+[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3};
+[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4};
+[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5};
+[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6};
constexpr u64 NO_PROCESS_FOUND_PID{0};
@@ -95,18 +100,18 @@ public:
private:
void GetProcessId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto title_id = rp.PopRaw<u64>();
+ const auto program_id = rp.PopRaw<u64>();
- LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id);
+ LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
const auto process =
- SearchProcessList(kernel.GetProcessList(), [title_id](const auto& proc) {
- return proc->GetProgramID() == title_id;
+ SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) {
+ return proc->GetProgramID() == program_id;
});
if (!process.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_PROCESS_NOT_FOUND);
+ rb.Push(ResultProcessNotFound);
return;
}
@@ -128,13 +133,16 @@ public:
explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_)
: ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
static const FunctionInfo functions[] = {
- {0, &Info::GetTitleId, "GetTitleId"},
+ {0, &Info::GetProgramId, "GetProgramId"},
+ {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"},
+ {65001, nullptr, "AtmosphereHasLaunchedProgram"},
+ {65002, nullptr, "AtmosphereGetProcessInfo"},
};
RegisterHandlers(functions);
}
private:
- void GetTitleId(Kernel::HLERequestContext& ctx) {
+ void GetProgramId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
@@ -146,7 +154,7 @@ private:
if (!process.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_PROCESS_NOT_FOUND);
+ rb.Push(ResultProcessNotFound);
return;
}
@@ -155,6 +163,27 @@ private:
rb.Push((*process)->GetProgramID());
}
+ void AtmosphereGetProcessId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto program_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
+
+ const auto process = SearchProcessList(process_list, [program_id](const auto& proc) {
+ return proc->GetProgramID() == program_id;
+ });
+
+ if (!process.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultProcessNotFound);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push((*process)->GetProcessID());
+ }
+
const std::vector<Kernel::KProcess*>& process_list;
};
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
}
metadata.Print();
- const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
- "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"};
+ const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
+ "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
+ "subsdk8", "subsdk9", "sdk"};
// Use the NSO module loader to figure out the code layout
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 @@
namespace Core::Memory {
namespace {
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
-constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) {
auto end_index = start_index;
@@ -61,7 +60,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
applet_resource
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
.GetAndResetPressState();
- return press_state & KEYPAD_BITMASK;
+ return static_cast<u64>(press_state & HID::NpadButton::All);
}
void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index a2541906f..816202588 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -33,7 +33,7 @@ public:
explicit PerfStats(u64 title_id_);
~PerfStats();
- using Clock = std::chrono::high_resolution_clock;
+ using Clock = std::chrono::steady_clock;
void BeginSystemFrame();
void EndSystemFrame();
@@ -87,7 +87,7 @@ private:
class SpeedLimiter {
public:
- using Clock = std::chrono::high_resolution_clock;
+ using Clock = std::chrono::steady_clock;
void DoSpeedLimiting(std::chrono::microseconds current_system_time_us);