summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt19
-rw-r--r--src/core/core.cpp14
-rw-r--r--src/core/core.h10
-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/input.h217
-rw-r--r--src/core/hid/emulated_console.cpp229
-rw-r--r--src/core/hid/emulated_console.h188
-rw-r--r--src/core/hid/emulated_controller.cpp1061
-rw-r--r--src/core/hid/emulated_controller.h392
-rw-r--r--src/core/hid/emulated_devices.cpp451
-rw-r--r--src/core/hid/emulated_devices.h209
-rw-r--r--src/core/hid/hid_core.cpp168
-rw-r--r--src/core/hid/hid_core.h73
-rw-r--r--src/core/hid/hid_types.h631
-rw-r--r--src/core/hid/input_converter.cpp383
-rw-r--r--src/core/hid/input_converter.h95
-rw-r--r--src/core/hid/input_interpreter.cpp (renamed from src/core/frontend/input_interpreter.cpp)25
-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/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/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.cpp1617
-rw-r--r--src/core/hle/service/hid/controllers/npad.h649
-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.cpp394
-rw-r--r--src/core/hle/service/hid/hid.h33
-rw-r--r--src/core/hle/service/hid/ring_lifo.h54
49 files changed, 6166 insertions, 2611 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9f0fbba2d..582c15f7e 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
@@ -402,6 +414,7 @@ add_library(core STATIC
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
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 07448fd29..473ab9f81 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;
@@ -615,6 +617,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 +835,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..645e5c241 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;
@@ -285,6 +289,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/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/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..80db8e9c6
--- /dev/null
+++ b/src/core/hid/emulated_console.cpp
@@ -0,0 +1,229 @@
+// 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) {
+ Common::Input::InputCallback motion_callback{
+ [this](Common::Input::CallbackStatus callback) { SetMotion(callback); }};
+ motion_devices->SetCallback(motion_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;
+ }
+ Common::Input::InputCallback touch_callback{
+ [this, index](Common::Input::CallbackStatus callback) { SetTouch(callback, index); }};
+ touch_device->SetCallback(touch_callback);
+ 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(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(Common::Input::CallbackStatus callback,
+ [[maybe_unused]] 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..25c183eee
--- /dev/null
+++ b/src/core/hid/emulated_console.h
@@ -0,0 +1,188 @@
+// 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 related to the console like motion and touch input
+ */
+ 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. Locking all HID service events from being
+ /// moddified
+ void EnableConfiguration();
+
+ /// Returns the emulated console to the normal behaivour
+ void DisableConfiguration();
+
+ /// Returns true if the emulated console is on 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 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 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 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 A CallbackStatus containing gyro and accelerometer data
+ */
+ void SetMotion(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(Common::Input::CallbackStatus callback, std::size_t index);
+
+ /**
+ * Triggers a callback that something has changed on the console status
+ * @param 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..06ae41c3e
--- /dev/null
+++ b/src/core/hid/emulated_controller.cpp
@@ -0,0 +1,1061 @@
+// 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;
+ 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;
+ 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", "")};
+ Common::Input::InputCallback button_callback{
+ [this, index, uuid](Common::Input::CallbackStatus callback) {
+ SetButton(callback, index, uuid);
+ }};
+ button_devices[index]->SetCallback(button_callback);
+ 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", "")};
+ Common::Input::InputCallback stick_callback{
+ [this, index, uuid](Common::Input::CallbackStatus callback) {
+ SetStick(callback, index, uuid);
+ }};
+ stick_devices[index]->SetCallback(stick_callback);
+ 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", "")};
+ Common::Input::InputCallback trigger_callback{
+ [this, index, uuid](Common::Input::CallbackStatus callback) {
+ SetTrigger(callback, index, uuid);
+ }};
+ trigger_devices[index]->SetCallback(trigger_callback);
+ trigger_devices[index]->ForceUpdate();
+ }
+
+ for (std::size_t index = 0; index < battery_devices.size(); ++index) {
+ if (!battery_devices[index]) {
+ continue;
+ }
+ Common::Input::InputCallback battery_callback{
+ [this, index](Common::Input::CallbackStatus callback) { SetBattery(callback, index); }};
+ battery_devices[index]->SetCallback(battery_callback);
+ battery_devices[index]->ForceUpdate();
+ }
+
+ for (std::size_t index = 0; index < motion_devices.size(); ++index) {
+ if (!motion_devices[index]) {
+ continue;
+ }
+ Common::Input::InputCallback motion_callback{
+ [this, index](Common::Input::CallbackStatus callback) { SetMotion(callback, index); }};
+ motion_devices[index]->SetCallback(motion_callback);
+ 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;
+ }
+ Common::Input::InputCallback button_callback{
+ [this, index, tas_uuid](Common::Input::CallbackStatus callback) {
+ SetButton(callback, index, tas_uuid);
+ }};
+ tas_button_devices[index]->SetCallback(button_callback);
+ }
+
+ for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
+ if (!tas_stick_devices[index]) {
+ continue;
+ }
+ Common::Input::InputCallback stick_callback{
+ [this, index, tas_uuid](Common::Input::CallbackStatus callback) {
+ SetStick(callback, index, tas_uuid);
+ }};
+ tas_stick_devices[index]->SetCallback(stick_callback);
+ }
+}
+
+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] = param;
+ ReloadInput();
+}
+
+void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) {
+ if (index >= stick_params.size()) {
+ return;
+ }
+ stick_params[index] = param;
+ ReloadInput();
+}
+
+void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
+ if (index >= motion_params.size()) {
+ return;
+ }
+ motion_params[index] = param;
+ ReloadInput();
+}
+
+void EmulatedController::SetButton(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(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(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.stick_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(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(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::Connect() {
+ {
+ 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, 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..2c5d51bc8
--- /dev/null
+++ b/src/core/hid/emulated_controller.h
@@ -0,0 +1,392 @@
+// 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 related to this controller. Like buttons, joysticks, motion.
+ * @param 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 If true tmp_npad_type will be returned
+ * @return NpadStyleIndex set on the controller
+ */
+ NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
+
+ /// Sets the connected status to true
+ void Connect();
+
+ /// Sets the connected status to false
+ void Disconnect();
+
+ /**
+ * Is the emulated connected
+ * @param 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 console into configuring mode. Locking all HID service events from being
+ /// moddified
+ void EnableConfiguration();
+
+ /// Returns the emulated console to the normal behaivour
+ void DisableConfiguration();
+
+ /// Returns true if the emulated device is on 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 ParamPackage with controller data to be mapped
+ */
+ void SetButtonParam(std::size_t index, Common::ParamPackage param);
+
+ /**
+ * Updates the current mapped stick device
+ * @param ParamPackage with controller data to be mapped
+ */
+ void SetStickParam(std::size_t index, Common::ParamPackage param);
+
+ /**
+ * Updates the current mapped motion device
+ * @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 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 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();
+
+ /**
+ * 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(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(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(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(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(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 be sended to only services
+ */
+ void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
+
+ NpadIdType npad_id_type;
+ NpadStyleIndex npad_type{NpadStyleIndex::None};
+ 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 on configuration 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..874780ec2
--- /dev/null
+++ b/src/core/hid/emulated_devices.cpp
@@ -0,0 +1,451 @@
+// 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;
+ }
+ Common::Input::InputCallback button_callback{
+ [this, index](Common::Input::CallbackStatus callback) {
+ SetMouseButton(callback, index);
+ }};
+ mouse_button_devices[index]->SetCallback(button_callback);
+ }
+
+ for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) {
+ if (!mouse_analog_devices[index]) {
+ continue;
+ }
+ Common::Input::InputCallback button_callback{
+ [this, index](Common::Input::CallbackStatus callback) {
+ SetMouseAnalog(callback, index);
+ }};
+ mouse_analog_devices[index]->SetCallback(button_callback);
+ }
+
+ if (mouse_stick_device) {
+ Common::Input::InputCallback button_callback{
+ [this](Common::Input::CallbackStatus callback) { SetMouseStick(callback); }};
+ mouse_stick_device->SetCallback(button_callback);
+ }
+
+ for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
+ if (!keyboard_devices[index]) {
+ continue;
+ }
+ Common::Input::InputCallback button_callback{
+ [this, index](Common::Input::CallbackStatus callback) {
+ SetKeyboardButton(callback, index);
+ }};
+ keyboard_devices[index]->SetCallback(button_callback);
+ }
+
+ for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
+ if (!keyboard_modifier_devices[index]) {
+ continue;
+ }
+ Common::Input::InputCallback button_callback{
+ [this, index](Common::Input::CallbackStatus callback) {
+ SetKeyboardModifier(callback, index);
+ }};
+ keyboard_modifier_devices[index]->SetCallback(button_callback);
+ }
+}
+
+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(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(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(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(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(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, 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..05a945d08
--- /dev/null
+++ b/src/core/hid/emulated_devices.h
@@ -0,0 +1,209 @@
+// 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
+ * like keyboard and mouse
+ */
+ EmulatedDevices();
+ ~EmulatedDevices();
+
+ YUZU_NON_COPYABLE(EmulatedDevices);
+ YUZU_NON_MOVEABLE(EmulatedDevices);
+
+ /// Removes all callbacks created from input devices
+ void UnloadInput();
+
+ /// Sets the emulated console into configuring mode. Locking all HID service events from being
+ /// moddified
+ void EnableConfiguration();
+
+ /// Returns the emulated console to the normal behaivour
+ void DisableConfiguration();
+
+ /// Returns true if the emulated device is on 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 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 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(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(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(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(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
+ * @param index: stick ID to be updated
+ */
+ void SetMouseStick(Common::Input::CallbackStatus callback);
+
+ /**
+ * Triggers a callback that something has changed on the device status
+ * @param 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..741a69c3c
--- /dev/null
+++ b/src/core/hid/hid_core.cpp
@@ -0,0 +1,168 @@
+// 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;
+}
+
+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;
+}
+
+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..609f40f3b
--- /dev/null
+++ b/src/core/hid/hid_core.h
@@ -0,0 +1,73 @@
+// 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;
+
+ /// 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;
+};
+
+} // 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..acf54e233
--- /dev/null
+++ b/src/core/hid/hid_types.h
@@ -0,0 +1,631 @@
+// 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,
+};
+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,
+};
+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..1492489d7
--- /dev/null
+++ b/src/core/hid/input_converter.h
@@ -0,0 +1,95 @@
+// 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 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 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 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 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 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 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 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 An analog object containing raw data and properties, bool that 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 Two analog objects containing raw data and properties, 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..870422d82 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 u64 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] = 0xFFFFFFFFFFFFFFFF;
for (std::size_t i = 1; i < button_states.size(); ++i) {
button_states[i] = 0;
}
}
-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 (button_states[current_index] & static_cast<u64>(button)) != 0;
}
-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 = (button_states[current_index] & static_cast<u64>(button)) != 0;
+ const bool previous_press = (button_states[previous_index] & static_cast<u64>(button)) != 0;
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 {
+ u64 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 (held_buttons & static_cast<u64>(button)) != 0;
}
diff --git a/src/core/frontend/input_interpreter.h b/src/core/hid/input_interpreter.h
index 9495e3daf..1c2e02142 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<u64, 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/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/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..dd4d954aa 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,131 +47,215 @@ 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;
+}
+
+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::System& system_,
+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;
}
- controller.style_set.raw = 0; // Zero out
- controller.device_type.raw = 0;
- controller.system_properties.raw = 0;
+ 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.is_connected) {
+ 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) {
+ LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
+ auto& controller = GetControllerFromNpadIdType(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.assignment_mode = NpadJoyAssignmentMode::Single;
+ shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
+ break;
+ 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);
+ shared_memory.device_type.joycon_left.Assign(1);
+ shared_memory.device_type.joycon_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::JoyDual;
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::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.assignment_mode = NpadJoyAssignmentMode::Single;
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
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::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.assignment_mode = NpadJoyAssignmentMode::Single;
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
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::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::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::Pokeball:
+ shared_memory.style_tag.palma.Assign(1);
+ shared_memory.device_type.palma.Assign(1);
+ shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
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::NES:
+ shared_memory.style_tag.lark.Assign(1);
+ shared_memory.device_type.fullkey.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::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();
+ 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));
+ }
- if (style.raw == 0) {
+ if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
// We want to support all controllers
+ Core::HID::NpadStyleTag style{};
style.handheld.Assign(1);
style.joycon_left.Assign(1);
style.joycon_right.Assign(1);
@@ -273,173 +263,120 @@ void Controller_NPad::OnInit() {
style.fullkey.Assign(1);
style.gamecube.Assign(1);
style.palma.Assign(1);
+ hid_core.SetSupportedStyleTag(style);
}
- 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};
+ 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(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);
}
}
- // Account for handheld
- if (connected_controllers[HANDHELD_INDEX].is_connected) {
- connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
+ // Connect controllers
+ for (auto& controller : controller_data) {
+ const auto& device = controller.device;
+ if (device->IsConnected()) {
+ AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
+ }
}
+}
- 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));
+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);
+}
- 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);
+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::OnLoadInputDevices() {
- const auto& players = Settings::values.players.GetValue();
-
+void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
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);
- }
+ auto& controller = GetControllerFromNpadIdType(npad_id);
+ const auto controller_type = controller.device->GetNpadStyleIndex();
+ if (!controller.device->IsConnected()) {
+ return;
}
-}
-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, {});
- }
+ 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;
}
- for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
- service_context.CloseEvent(styleset_changed_events[i]);
+ 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;
}
-}
-void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
- std::lock_guard lock{mutex};
+ 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);
+ }
- 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::JoyconRight) {
+ pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
+ pad_entry.npad_buttons.right_sr.Assign(button_state.right_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::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 +385,132 @@ 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];
+ for (std::size_t i = 0; i < controller_data.size(); ++i) {
+ auto& controller = controller_data[i];
+ auto& npad = controller.shared_memory_entry;
- analog_trigger->timestamp = core_timing.GetCPUTicks();
- analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
+ const auto& controller_type = controller.device->GetNpadStyleIndex();
- auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
-
- cur_entry.timestamp = last_entry.timestamp + 1;
- cur_entry.timestamp2 = cur_entry.timestamp;
- }
-
- const auto& controller_type = connected_controllers[i].type;
-
- 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 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::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::JoyconDual:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_left_connected.Assign(1);
+ pad_state.connection_status.is_right_connected.Assign(1);
+
+ libnx_state.connection_status.is_left_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::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::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::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::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::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::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::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::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;
- 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;
+ 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<u32>(pad_state.pad_states.raw);
+ press_state |= static_cast<u32>(pad_state.npad_buttons.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 +518,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 (std::size_t i = 0; i < controller_data.size(); ++i) {
+ auto& controller = controller_data[i];
- for (auto* sixaxis_sensor : controller_sixaxes) {
- sixaxis_sensor->common.entry_count = 16;
- sixaxis_sensor->common.total_entry_count = 17;
+ const auto& controller_type = controller.device->GetNpadStyleIndex();
- 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;
-
- auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
-
- 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 {
+ // Hanheld 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 +668,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 +692,35 @@ 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,
+ 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;
}
}
-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 +734,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 +761,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,167 +814,231 @@ 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);
+ DisconnectNpad(npad_id);
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);
- 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& shared_memory_entry = controller.shared_memory_entry;
+ 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.assignment_mode = NpadJoyAssignmentMode::Dual;
+ shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
- 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;
+ 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).device;
+ auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
// 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_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) ||
+ (controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight)) {
// 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);
+ AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
}
}
@@ -1092,61 +1052,61 @@ 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);
+ 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();
- if (!IsControllerSupported(connected_controllers[npad_index_1].type) ||
- !IsControllerSupported(connected_controllers[npad_index_2].type)) {
+ if (!IsControllerSupported(type_index_1) || !IsControllerSupported(type_index_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);
+ AddNewControllerAt(type_index_2, npad_id_1);
+ AddNewControllerAt(type_index_1, npad_id_2);
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,32 +1114,34 @@ 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);
}
}
@@ -1187,16 +1149,16 @@ u32 Controller_NPad::GetAndResetPressState() {
return 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 +1167,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 +1200,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..9fa113bb6 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,106 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
- void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
+ void SetNpadMode(Core::HID::NpadIdType npad_id, 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();
- 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 +221,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 +292,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 +322,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 +370,150 @@ 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);
+ 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{};
+ Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
+
+ // 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<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>;
-
+ 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..95fc07325 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)
@@ -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,15 +966,16 @@ 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::NpadJoyAssignmentMode::Single);
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
parameters.npad_id, parameters.applet_resource_user_id);
@@ -937,16 +988,17 @@ 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;
};
+ 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, Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_WARNING(Service_HID,
"(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
@@ -960,15 +1012,16 @@ 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);
@@ -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.");
@@ -1132,38 +1188,38 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
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 +1234,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 +1259,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 +1314,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 +1334,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 +1351,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 +1395,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 +1406,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 +1416,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 +1460,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 +1495,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 +1515,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 +1680,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);
@@ -2037,10 +2095,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..ab0084118 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)>
@@ -161,38 +163,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