// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include "common/bit_field.h" #include "common/common_types.h" #include "common/vector_math.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; class KReadableEvent; } // namespace Kernel namespace Service::KernelHelpers { class ServiceContext; } // namespace Service::KernelHelpers union Result; namespace Service::HID { class Controller_NPad final : public ControllerBase { public: explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, KernelHelpers::ServiceContext& service_context_); ~Controller_NPad() override; // Called when the controller is initialized void OnInit() override; // When the controller is released void OnRelease() override; // When the controller is requesting an update for the shared memory void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; // When the controller is requesting a motion update for the shared memory void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override; // This is nn::hid::NpadJoyHoldType enum class NpadJoyHoldType : u64 { Vertical = 0, Horizontal = 1, }; // 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, MaxActivationMode = 3, }; // This is nn::hid::NpadCommunicationMode enum class NpadCommunicationMode : u64 { Mode_5ms = 0, Mode_10ms = 1, Mode_15ms = 2, Default = 3, }; void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); Core::HID::NpadStyleTag GetSupportedStyleSet() const; Result SetSupportedNpadIdTypes(std::span data); void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); std::size_t GetSupportedNpadIdTypesSize() const; void SetHoldType(NpadJoyHoldType joy_hold_type); NpadJoyHoldType GetHoldType() const; void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); NpadHandheldActivationMode GetNpadHandheldActivationMode() const; void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); NpadCommunicationMode GetNpadCommunicationMode() const; bool SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode); bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, const Core::HID::VibrationValue& vibration_value); void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, const Core::HID::VibrationValue& vibration_value); void VibrateControllers( const std::vector& vibration_device_handles, const std::vector& vibration_values); Core::HID::VibrationValue GetLastVibration( const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle); void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index); void SetPermitVibrationSession(bool permit_vibration_session); bool IsVibrationDeviceMounted( const Core::HID::VibrationDeviceHandle& vibration_device_handle) 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(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id); // Adds a new controller at an index with connection status. void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, bool connected); Result DisconnectNpad(Core::HID::NpadIdType npad_id); Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::GyroscopeZeroDriftMode drift_mode); Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::GyroscopeZeroDriftMode& drift_mode) const; Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_at_rest) const; Result IsFirmwareUpdateAvailableForSixAxisSensor( const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; Result EnableSixAxisSensorUnalteredPassthrough( const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); Result IsSixAxisSensorUnalteredPassthroughEnabled( const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; Result LoadSixAxisSensorCalibrationParameter( const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::SixAxisSensorCalibrationParameter& calibration) const; Result GetSixAxisSensorIcInformation( const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::SixAxisSensorIcInformation& ic_information) const; Result ResetIsSixAxisSensorDeviceNewlyAssigned( const Core::HID::SixAxisSensorHandle& sixaxis_handle); Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool sixaxis_status); Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const; Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled); Result SetSixAxisFusionParameters( const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, Core::HID::SixAxisSensorFusionParameters& parameters) const; Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, bool& is_enabled) const; Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, Core::HID::NpadIdType npad_id); void SetAnalogStickUseCenterClamp(bool use_center_clamp); void ClearAllConnectedControllers(); void DisconnectAllConnectedControllers(); void ConnectAllDisconnectedControllers(); void ClearAllControllers(); Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); void StartLRAssignmentMode(); void StopLRAssignmentMode(); Result 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. Core::HID::NpadButton GetAndResetPressState(); static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); static Result VerifyValidSixAxisSensorHandle( const Core::HID::SixAxisSensorHandle& device_handle); private: static constexpr std::size_t NPAD_COUNT = 10; // This is nn::hid::detail::ColorAttribute enum class ColorAttribute : u32 { Ok = 0, ReadError = 1, NoController = 2, }; static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size"); // This is nn::hid::detail::NpadFullKeyColorState struct NpadFullKeyColorState { ColorAttribute attribute{ColorAttribute::NoController}; Core::HID::NpadControllerColor fullkey{}; }; static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); // This is nn::hid::detail::NpadJoyColorState struct NpadJoyColorState { ColorAttribute attribute{ColorAttribute::NoController}; Core::HID::NpadControllerColor left{}; Core::HID::NpadControllerColor right{}; }; static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); // This is nn::hid::NpadAttribute struct NpadAttribute { union { u32 raw{}; BitField<0, 1, u32> is_connected; BitField<1, 1, u32> is_wired; BitField<2, 1, u32> is_left_connected; BitField<3, 1, u32> is_left_wired; BitField<4, 1, u32> is_right_connected; BitField<5, 1, u32> is_right_wired; }; }; 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(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); // This is nn::hid::SixAxisSensorAttribute struct SixAxisSensorAttribute { union { u32 raw{}; BitField<0, 1, u32> is_connected; BitField<1, 1, u32> is_interpolated; }; }; static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); // This is nn::hid::SixAxisSensorState struct SixAxisSensorState { s64 delta_time{}; s64 sampling_number{}; Common::Vec3f accel{}; Common::Vec3f gyro{}; Common::Vec3f rotation{}; std::array orientation{}; SixAxisSensorAttribute attribute{}; INSERT_PADDING_BYTES(4); // Reserved }; static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); // This is nn::hid::server::NpadGcTriggerState struct NpadGcTriggerState { s64 sampling_number{}; s32 l_analog{}; s32 r_analog{}; }; static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); // This is nn::hid::NpadSystemProperties struct NPadSystemProperties { union { 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; BitField<3, 1, s64> is_powered_joy_dual; BitField<4, 1, s64> is_powered_joy_left; BitField<5, 1, s64> is_powered_joy_right; BitField<9, 1, s64> is_system_unsupported_button; BitField<10, 1, s64> is_system_ext_unsupported_button; BitField<11, 1, s64> is_vertical; BitField<12, 1, s64> is_horizontal; BitField<13, 1, s64> use_plus; BitField<14, 1, s64> use_minus; BitField<15, 1, s64> use_directional_buttons; }; }; static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size"); // This is nn::hid::NpadSystemButtonProperties struct NpadSystemButtonProperties { union { s32 raw{}; BitField<0, 1, s32> is_home_button_protection_enabled; }; }; static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size"); // This is nn::hid::system::DeviceType struct DeviceType { union { u32 raw{}; BitField<0, 1, s32> fullkey; BitField<1, 1, s32> debug_pad; BitField<2, 1, s32> handheld_left; BitField<3, 1, s32> handheld_right; BitField<4, 1, s32> joycon_left; BitField<5, 1, s32> joycon_right; BitField<6, 1, s32> palma; BitField<7, 1, s32> lark_hvc_left; BitField<8, 1, s32> lark_hvc_right; BitField<9, 1, s32> lark_nes_left; BitField<10, 1, s32> lark_nes_right; BitField<11, 1, s32> handheld_lark_hvc_left; BitField<12, 1, s32> handheld_lark_hvc_right; 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; }; }; // 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, HandheldJoyConLeftOnly = 1, HandheldJoyConRightOnly = 3, HandheldJoyConLeftJoyConRight = 4, JoyDual = 5, JoyDualLeftOnly = 6, JoyDualRightOnly = 7, JoyLeftHorizontal = 8, JoyLeftVertical = 9, JoyRightHorizontal = 10, JoyRightVertical = 11, SwitchProController = 12, CompatibleProController = 13, CompatibleJoyCon = 14, LarkHvc1 = 15, LarkHvc2 = 16, LarkNesLeft = 17, LarkNesRight = 18, Lucia = 19, Verification = 20, Lagon = 21, }; struct AppletFooterUi { AppletFooterUiAttributes attributes{}; AppletFooterUiType type{AppletFooterUiType::None}; 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, }; struct AppletNfcXcd { union { AppletFooterUi applet_footer{}; Lifo nfc_xcd_device_lifo; }; }; // This is nn::hid::detail::NpadInternalState struct NpadInternalState { Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; NpadFullKeyColorState fullkey_color{}; NpadJoyColorState joycon_color{}; Lifo fullkey_lifo{}; Lifo handheld_lifo{}; Lifo joy_dual_lifo{}; Lifo joy_left_lifo{}; Lifo joy_right_lifo{}; Lifo palma_lifo{}; Lifo system_ext_lifo{}; Lifo sixaxis_fullkey_lifo{}; Lifo sixaxis_handheld_lifo{}; Lifo sixaxis_dual_left_lifo{}; Lifo sixaxis_dual_right_lifo{}; Lifo sixaxis_left_lifo{}; Lifo sixaxis_right_lifo{}; DeviceType device_type{}; INSERT_PADDING_BYTES(0x4); // Reserved NPadSystemProperties system_properties{}; NpadSystemButtonProperties button_properties{}; Core::HID::NpadBatteryLevel battery_level_dual{}; Core::HID::NpadBatteryLevel battery_level_left{}; Core::HID::NpadBatteryLevel battery_level_right{}; AppletNfcXcd applet_nfc_xcd{}; INSERT_PADDING_BYTES(0x20); // Unknown Lifo gc_trigger_lifo{}; NpadLarkType lark_type_l_and_main{}; NpadLarkType lark_type_r{}; NpadLuciaType lucia_type{}; NpadLagonType lagon_type{}; NpadLagerType lager_type{}; Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties; Core::HID::SixAxisSensorProperties sixaxis_handheld_properties; Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties; Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties; Core::HID::SixAxisSensorProperties sixaxis_left_properties; Core::HID::SixAxisSensorProperties sixaxis_right_properties; INSERT_PADDING_BYTES(0xc06); // 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 SixaxisParameters { bool is_fusion_enabled{true}; bool unaltered_passtrough{false}; Core::HID::SixAxisSensorFusionParameters fusion{}; Core::HID::SixAxisSensorCalibrationParameter calibration{}; Core::HID::SixAxisSensorIcInformation ic_information{}; Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{ Core::HID::GyroscopeZeroDriftMode::Standard}; }; struct NpadControllerData { Kernel::KEvent* styleset_changed_event{}; NpadInternalState* shared_memory = nullptr; Core::HID::EmulatedController* device = nullptr; std::array vibration{}; bool unintended_home_button_input_protection{}; bool is_connected{}; // Dual joycons can have only one side connected bool is_dual_left_connected{true}; bool is_dual_right_connected{true}; // Motion parameters bool sixaxis_at_rest{true}; bool sixaxis_sensor_enabled{true}; SixaxisParameters sixaxis_fullkey{}; SixaxisParameters sixaxis_handheld{}; SixaxisParameters sixaxis_dual_left{}; SixaxisParameters sixaxis_dual_right{}; SixaxisParameters sixaxis_left{}; SixaxisParameters sixaxis_right{}; SixaxisParameters sixaxis_unknown{}; // 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; Core::HID::SixAxisSensorProperties& GetSixaxisProperties( const Core::HID::SixAxisSensorHandle& device_handle); const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( const Core::HID::SixAxisSensorHandle& device_handle) const; SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); const SixaxisParameters& GetSixaxisState( const Core::HID::SixAxisSensorHandle& device_handle) const; std::atomic press_state{}; std::array controller_data{}; KernelHelpers::ServiceContext& service_context; std::mutex mutex; std::vector supported_npad_id_types{}; NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical}; NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; bool permit_vibration_session_enabled{false}; bool analog_stick_use_center_clamp{false}; bool is_in_lr_assignment_mode{false}; bool is_controller_initialized{false}; }; } // namespace Service::HID