code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <atomic>
#include <mutex>
#include <span>

#include "common/common_types.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/abstracted_pad/abstract_pad.h"
#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/npad/npad_resource.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/vibration/gc_vibration_device.h"
#include "hid_core/resources/vibration/n64_vibration_device.h"
#include "hid_core/resources/vibration/vibration_base.h"
#include "hid_core/resources/vibration/vibration_device.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

namespace Service::Set {
class ISystemSettingsServer;
}

union Result;

namespace Service::HID {
class AppletResource;
struct HandheldConfig;
struct NpadInternalState;
struct NpadSixAxisSensorLifo;
struct NpadSharedMemoryFormat;

class NPad final {
public:
    explicit NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_);
    ~NPad();

    Result Activate();
    Result Activate(u64 aruid);

    Result ActivateNpadResource();
    Result ActivateNpadResource(u64 aruid);

    void FreeAppletResourceId(u64 aruid);

    // When the controller is requesting an update for the shared memory
    void OnUpdate(const Core::Timing::CoreTiming& core_timing);

    Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set);
    Result GetSupportedNpadStyleSet(u64 aruid,
                                    Core::HID::NpadStyleSet& out_supported_style_set) const;
    Result GetMaskedSupportedNpadStyleSet(u64 aruid,
                                          Core::HID::NpadStyleSet& out_supported_style_set) const;

    Result SetSupportedNpadIdType(u64 aruid,
                                  std::span<const Core::HID::NpadIdType> supported_npad_list);

    Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type);
    Result GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const;

    Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode);
    Result GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const;

    bool SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
                     NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode);

    Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
                                                Core::HID::NpadIdType npad_id);

    // Adds a new controller at an index.
    void AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
                            Core::HID::NpadIdType npad_id);
    // Adds a new controller at an index with connection status.
    void UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
                            Core::HID::NpadIdType npad_id, bool connected);

    Result DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id);

    Result IsFirmwareUpdateAvailableForSixAxisSensor(
        u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle,
        bool& is_firmware_available) const;
    Result ResetIsSixAxisSensorDeviceNewlyAssigned(
        u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle);

    Result IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid,
                                                        Core::HID::NpadIdType npad_id) const;
    Result EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id,
                                                     bool is_enabled);

    void SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled);
    void ClearAllConnectedControllers();
    void DisconnectAllConnectedControllers();
    void ConnectAllDisconnectedControllers();
    void ClearAllControllers();

    Result MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1,
                                   Core::HID::NpadIdType npad_id_2);
    Result StartLrAssignmentMode(u64 aruid);
    Result StopLrAssignmentMode(u64 aruid);
    Result SwapNpadAssignment(u64 aruid, 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();

    Result ApplyNpadSystemCommonPolicy(u64 aruid);
    Result ApplyNpadSystemCommonPolicyFull(u64 aruid);
    Result ClearNpadSystemCommonPolicy(u64 aruid);

    void SetRevision(u64 aruid, NpadRevision revision);
    NpadRevision GetRevision(u64 aruid);

    Result RegisterAppletResourceUserId(u64 aruid);
    void UnregisterAppletResourceUserId(u64 aruid);
    void SetNpadExternals(std::shared_ptr<AppletResource> resource,
                          std::recursive_mutex* shared_mutex,
                          std::shared_ptr<HandheldConfig> handheld_config,
                          Kernel::KEvent* input_event, std::mutex* input_mutex,
                          std::shared_ptr<Service::Set::ISystemSettingsServer> settings);

    AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);

    Result SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set,
                                          Core::HID::NpadButton button_assignment);
    Result ClearNpadCaptureButtonAssignment(u64 aruid);
    std::size_t GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list,
                                               u64 aruid) const;

    Result SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled);

    Result AssigningSingleOnSlSrPress(u64 aruid, bool is_enabled);

    Result GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const;

    NpadVibration* GetVibrationHandler();
    std::vector<NpadVibrationBase*> GetAllVibrationDevices();
    NpadVibrationBase* GetVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
    NpadN64VibrationDevice* GetN64VibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
    NpadVibrationDevice* GetNSVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
    NpadGcVibrationDevice* GetGcVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);

    void UpdateHandheldAbstractState();

    void EnableAppletToGetInput(u64 aruid);

private:
    struct NpadControllerData {
        NpadInternalState* shared_memory = nullptr;
        Core::HID::EmulatedController* device = nullptr;

        bool is_active{};
        bool is_connected{};

        // Dual joycons can have only one side connected
        bool is_dual_left_connected{true};
        bool is_dual_right_connected{true};

        // Current pad state
        NPadGenericState npad_pad_state{};
        NPadGenericState npad_libnx_state{};
        NpadGcTriggerState npad_trigger_state{};
        int callback_key{};
    };

    void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
    void InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id);
    void RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id);
    void WriteEmptyEntry(NpadInternalState* npad);

    NpadControllerData& GetControllerFromHandle(
        u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle);
    const NpadControllerData& GetControllerFromHandle(
        u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const;
    NpadControllerData& GetControllerFromNpadIdType(u64 aruid, Core::HID::NpadIdType npad_id);
    const NpadControllerData& GetControllerFromNpadIdType(u64 aruid,
                                                          Core::HID::NpadIdType npad_id) const;

    Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
        u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle);
    const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
        u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const;

    Core::HID::HIDCore& hid_core;
    KernelHelpers::ServiceContext& service_context;

    s32 ref_counter{};
    mutable std::mutex mutex;
    NPadResource npad_resource;
    AppletResourceHolder applet_resource_holder{};
    std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads;
    NpadVibration vibration_handler{};

    std::atomic<u64> press_state{};
    std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
        controller_data{};
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "hid_core/hid_util.h"
#include "hid_core/resources/npad/npad_data.h"

namespace Service::HID {

NPadData::NPadData() {
    ClearNpadSystemCommonPolicy();
}

NPadData::~NPadData() = default;

NpadStatus NPadData::GetNpadStatus() const {
    return status;
}

void NPadData::SetNpadAnalogStickUseCenterClamp(bool is_enabled) {
    status.use_center_clamp.Assign(is_enabled);
}

bool NPadData::GetNpadAnalogStickUseCenterClamp() const {
    return status.use_center_clamp.As<bool>();
}

void NPadData::SetNpadSystemExtStateEnabled(bool is_enabled) {
    status.system_ext_state.Assign(is_enabled);
}

bool NPadData::GetNpadSystemExtState() const {
    return status.system_ext_state.As<bool>();
}

Result NPadData::SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list) {
    // Note: Real limit is 11. But array size is 10. N's bug?
    if (list.size() > MaxSupportedNpadIdTypes) {
        return ResultInvalidArraySize;
    }

    supported_npad_id_types_count = list.size();
    memcpy(supported_npad_id_types.data(), list.data(),
           list.size() * sizeof(Core::HID::NpadIdType));

    return ResultSuccess;
}

std::size_t NPadData::GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const {
    std::size_t out_size = std::min(supported_npad_id_types_count, out_list.size());

    memcpy(out_list.data(), supported_npad_id_types.data(),
           out_size * sizeof(Core::HID::NpadIdType));

    return out_size;
}

bool NPadData::IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const {
    for (std::size_t i = 0; i < supported_npad_id_types_count; i++) {
        if (supported_npad_id_types[i] == npad_id) {
            return true;
        }
    }

    return false;
}

void NPadData::SetNpadSystemCommonPolicy(bool is_full_policy) {
    supported_npad_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::JoyDual |
                               Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
    handheld_activation_mode = NpadHandheldActivationMode::Dual;

    status.is_supported_styleset_set.Assign(true);
    status.is_hold_type_set.Assign(true);
    status.lr_assignment_mode.Assign(false);
    status.is_policy.Assign(true);
    if (is_full_policy) {
        status.is_full_policy.Assign(true);
    }

    supported_npad_id_types_count = 10;
    supported_npad_id_types[0] = Core::HID::NpadIdType::Player1;
    supported_npad_id_types[1] = Core::HID::NpadIdType::Player2;
    supported_npad_id_types[2] = Core::HID::NpadIdType::Player3;
    supported_npad_id_types[3] = Core::HID::NpadIdType::Player4;
    supported_npad_id_types[4] = Core::HID::NpadIdType::Player5;
    supported_npad_id_types[5] = Core::HID::NpadIdType::Player6;
    supported_npad_id_types[6] = Core::HID::NpadIdType::Player7;
    supported_npad_id_types[7] = Core::HID::NpadIdType::Player8;
    supported_npad_id_types[8] = Core::HID::NpadIdType::Other;
    supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;

    for (auto& input_protection : is_unintended_home_button_input_protection) {
        input_protection = true;
    }
}

void NPadData::ClearNpadSystemCommonPolicy() {
    status.raw = 0;
    supported_npad_style_set = Core::HID::NpadStyleSet::All;
    npad_hold_type = NpadJoyHoldType::Vertical;
    handheld_activation_mode = NpadHandheldActivationMode::Dual;

    for (auto& button_assignment : npad_button_assignment) {
        button_assignment = Core::HID::NpadButton::None;
    }

    supported_npad_id_types_count = 10;
    supported_npad_id_types[0] = Core::HID::NpadIdType::Player1;
    supported_npad_id_types[1] = Core::HID::NpadIdType::Player2;
    supported_npad_id_types[2] = Core::HID::NpadIdType::Player3;
    supported_npad_id_types[3] = Core::HID::NpadIdType::Player4;
    supported_npad_id_types[4] = Core::HID::NpadIdType::Player5;
    supported_npad_id_types[5] = Core::HID::NpadIdType::Player6;
    supported_npad_id_types[6] = Core::HID::NpadIdType::Player7;
    supported_npad_id_types[7] = Core::HID::NpadIdType::Player8;
    supported_npad_id_types[8] = Core::HID::NpadIdType::Other;
    supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;

    for (auto& input_protection : is_unintended_home_button_input_protection) {
        input_protection = true;
    }
}

void NPadData::SetNpadJoyHoldType(NpadJoyHoldType hold_type) {
    npad_hold_type = hold_type;
    status.is_hold_type_set.Assign(true);
}

NpadJoyHoldType NPadData::GetNpadJoyHoldType() const {
    return npad_hold_type;
}

void NPadData::SetHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
    handheld_activation_mode = activation_mode;
}

NpadHandheldActivationMode NPadData::GetHandheldActivationMode() const {
    return handheld_activation_mode;
}

void NPadData::SetSupportedNpadStyleSet(Core::HID::NpadStyleSet style_set) {
    supported_npad_style_set = style_set;
    status.is_supported_styleset_set.Assign(true);
    status.is_hold_type_set.Assign(true);
}

Core::HID::NpadStyleSet NPadData::GetSupportedNpadStyleSet() const {
    return supported_npad_style_set;
}

bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const {
    Core::HID::NpadStyleTag style = {supported_npad_style_set};
    switch (style_index) {
    case Core::HID::NpadStyleIndex::Fullkey:
        return style.fullkey.As<bool>();
    case Core::HID::NpadStyleIndex::Handheld:
        return style.handheld.As<bool>();
    case Core::HID::NpadStyleIndex::JoyconDual:
        return style.joycon_dual.As<bool>();
    case Core::HID::NpadStyleIndex::JoyconLeft:
        return style.joycon_left.As<bool>();
    case Core::HID::NpadStyleIndex::JoyconRight:
        return style.joycon_right.As<bool>();
    case Core::HID::NpadStyleIndex::GameCube:
        return style.gamecube.As<bool>();
    case Core::HID::NpadStyleIndex::Pokeball:
        return style.palma.As<bool>();
    case Core::HID::NpadStyleIndex::NES:
        return style.lark.As<bool>();
    case Core::HID::NpadStyleIndex::SNES:
        return style.lucia.As<bool>();
    case Core::HID::NpadStyleIndex::N64:
        return style.lagoon.As<bool>();
    case Core::HID::NpadStyleIndex::SegaGenesis:
        return style.lager.As<bool>();
    default:
        return false;
    }
}

void NPadData::SetLrAssignmentMode(bool is_enabled) {
    status.lr_assignment_mode.Assign(is_enabled);
}

bool NPadData::GetLrAssignmentMode() const {
    return status.lr_assignment_mode.As<bool>();
}

void NPadData::SetAssigningSingleOnSlSrPress(bool is_enabled) {
    status.assigning_single_on_sl_sr_press.Assign(is_enabled);
}

bool NPadData::GetAssigningSingleOnSlSrPress() const {
    return status.assigning_single_on_sl_sr_press.As<bool>();
}

void NPadData::SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id) {
    is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)] = is_enabled;
}

bool NPadData::GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const {
    return is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)];
}

void NPadData::SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment,
                                          std::size_t style_index) {
    npad_button_assignment[style_index] = button_assignment;
}

Core::HID::NpadButton NPadData::GetCaptureButtonAssignment(std::size_t style_index) const {
    return npad_button_assignment[style_index];
}

std::size_t NPadData::GetNpadCaptureButtonAssignmentList(
    std::span<Core::HID::NpadButton> out_list) const {
    for (std::size_t i = 0; i < out_list.size(); i++) {
        Core::HID::NpadStyleSet style_set = GetStylesetByIndex(i);
        if ((style_set & supported_npad_style_set) == Core::HID::NpadStyleSet::None ||
            npad_button_assignment[i] == Core::HID::NpadButton::None) {
            return i;
        }
        out_list[i] = npad_button_assignment[i];
    }

    return out_list.size();
}

} // namespace Service::HID

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>
#include <span>

#include "common/common_types.h"
#include "core/hle/result.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/npad/npad_types.h"

namespace Service::HID {

struct NpadStatus {
    union {
        u32 raw{};

        BitField<0, 1, u32> is_supported_styleset_set;
        BitField<1, 1, u32> is_hold_type_set;
        BitField<2, 1, u32> lr_assignment_mode;
        BitField<3, 1, u32> assigning_single_on_sl_sr_press;
        BitField<4, 1, u32> is_full_policy;
        BitField<5, 1, u32> is_policy;
        BitField<6, 1, u32> use_center_clamp;
        BitField<7, 1, u32> system_ext_state;
    };
};
static_assert(sizeof(NpadStatus) == 4, "NpadStatus is an invalid size");

/// Handles Npad request from HID interfaces
class NPadData final {
public:
    explicit NPadData();
    ~NPadData();

    NpadStatus GetNpadStatus() const;

    void SetNpadAnalogStickUseCenterClamp(bool is_enabled);
    bool GetNpadAnalogStickUseCenterClamp() const;

    void SetNpadSystemExtStateEnabled(bool is_enabled);
    bool GetNpadSystemExtState() const;

    Result SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list);
    std::size_t GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const;
    bool IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const;

    void SetNpadSystemCommonPolicy(bool is_full_policy);
    void ClearNpadSystemCommonPolicy();

    void SetNpadJoyHoldType(NpadJoyHoldType hold_type);
    NpadJoyHoldType GetNpadJoyHoldType() const;

    void SetHandheldActivationMode(NpadHandheldActivationMode activation_mode);
    NpadHandheldActivationMode GetHandheldActivationMode() const;

    void SetSupportedNpadStyleSet(Core::HID::NpadStyleSet style_set);
    Core::HID::NpadStyleSet GetSupportedNpadStyleSet() const;
    bool IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const;

    void SetLrAssignmentMode(bool is_enabled);
    bool GetLrAssignmentMode() const;

    void SetAssigningSingleOnSlSrPress(bool is_enabled);
    bool GetAssigningSingleOnSlSrPress() const;

    void SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id);
    bool GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const;

    void SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment,
                                    std::size_t style_index);
    Core::HID::NpadButton GetCaptureButtonAssignment(std::size_t style_index) const;
    std::size_t GetNpadCaptureButtonAssignmentList(std::span<Core::HID::NpadButton> out_list) const;

private:
    NpadStatus status{};
    Core::HID::NpadStyleSet supported_npad_style_set{Core::HID::NpadStyleSet::All};
    NpadJoyHoldType npad_hold_type{NpadJoyHoldType::Vertical};
    NpadHandheldActivationMode handheld_activation_mode{};
    std::array<Core::HID::NpadIdType, MaxSupportedNpadIdTypes> supported_npad_id_types{};
    std::array<Core::HID::NpadButton, StyleIndexCount> npad_button_assignment{};
    std::size_t supported_npad_id_types_count{};
    std::array<bool, MaxSupportedNpadIdTypes> is_unintended_home_button_input_protection{};
};

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "hid_core/hid_result.h"
#include "hid_core/hid_util.h"
#include "hid_core/resources/npad/npad_resource.h"
#include "hid_core/resources/npad/npad_types.h"

namespace Service::HID {

NPadResource::NPadResource(KernelHelpers::ServiceContext& context) : service_context{context} {}

NPadResource::~NPadResource() = default;

Result NPadResource::RegisterAppletResourceUserId(u64 aruid) {
    const auto aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index < AruidIndexMax) {
        return ResultAruidAlreadyRegistered;
    }

    std::size_t data_index = AruidIndexMax;
    for (std::size_t i = 0; i < AruidIndexMax; i++) {
        if (!state[i].flag.is_initialized) {
            data_index = i;
            break;
        }
    }

    if (data_index == AruidIndexMax) {
        return ResultAruidNoAvailableEntries;
    }

    auto& aruid_data = state[data_index];

    aruid_data.aruid = aruid;
    aruid_data.flag.is_initialized.Assign(true);

    data_index = AruidIndexMax;
    for (std::size_t i = 0; i < AruidIndexMax; i++) {
        if (registration_list.flag[i] == RegistrationStatus::Initialized) {
            if (registration_list.aruid[i] != aruid) {
                continue;
            }
            data_index = i;
            break;
        }
        // TODO: Don't Handle pending delete here
        if (registration_list.flag[i] == RegistrationStatus::None ||
            registration_list.flag[i] == RegistrationStatus::PendingDelete) {
            data_index = i;
            break;
        }
    }

    if (data_index == AruidIndexMax) {
        return ResultSuccess;
    }

    registration_list.flag[data_index] = RegistrationStatus::Initialized;
    registration_list.aruid[data_index] = aruid;

    return ResultSuccess;
}

void NPadResource::UnregisterAppletResourceUserId(u64 aruid) {
    const u64 aruid_index = GetIndexFromAruid(aruid);

    FreeAppletResourceId(aruid);
    if (aruid_index < AruidIndexMax) {
        state[aruid_index] = {};
        registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete;
    }

    for (std::size_t i = 0; i < AruidIndexMax; i++) {
        if (registration_list.flag[i] == RegistrationStatus::Initialized) {
            active_data_aruid = registration_list.aruid[i];
        }
    }
}

void NPadResource::FreeAppletResourceId(u64 aruid) {
    const u64 aruid_index = GetIndexFromAruid(aruid);

    if (aruid_index >= AruidIndexMax) {
        return;
    }

    auto& aruid_data = state[aruid_index];

    aruid_data.flag.is_assigned.Assign(false);

    for (auto& controller_state : aruid_data.controller_state) {
        if (!controller_state.is_styleset_update_event_initialized) {
            continue;
        }
        service_context.CloseEvent(controller_state.style_set_update_event);
        controller_state.is_styleset_update_event_initialized = false;
    }
}

Result NPadResource::Activate(u64 aruid) {
    const u64 aruid_index = GetIndexFromAruid(aruid);

    if (aruid_index >= AruidIndexMax) {
        return ResultSuccess;
    }

    auto& state_data = state[aruid_index];

    if (state_data.flag.is_assigned) {
        return ResultAruidAlreadyRegistered;
    }

    state_data.flag.is_assigned.Assign(true);
    state_data.data.ClearNpadSystemCommonPolicy();
    state_data.npad_revision = NpadRevision::Revision0;
    state_data.button_config = {};

    if (active_data_aruid == aruid) {
        default_hold_type = active_data.GetNpadJoyHoldType();
        active_data.SetNpadJoyHoldType(default_hold_type);
    }
    return ResultSuccess;
}

Result NPadResource::Activate() {
    if (ref_counter == std::numeric_limits<s32>::max() - 1) {
        return ResultAppletResourceOverflow;
    }
    if (ref_counter == 0) {
        RegisterAppletResourceUserId(SystemAruid);
        Activate(SystemAruid);
    }
    ref_counter++;
    return ResultSuccess;
}

Result NPadResource::Deactivate() {
    if (ref_counter == 0) {
        return ResultAppletResourceNotInitialized;
    }

    UnregisterAppletResourceUserId(SystemAruid);
    ref_counter--;
    return ResultSuccess;
}

NPadData* NPadResource::GetActiveData() {
    return &active_data;
}

u64 NPadResource::GetActiveDataAruid() {
    return active_data_aruid;
}

void NPadResource::SetAppletResourceUserId(u64 aruid) {
    if (active_data_aruid == aruid) {
        return;
    }

    active_data_aruid = aruid;
    default_hold_type = active_data.GetNpadJoyHoldType();
    const u64 aruid_index = GetIndexFromAruid(aruid);

    if (aruid_index >= AruidIndexMax) {
        return;
    }

    auto& data = state[aruid_index].data;
    if (data.GetNpadStatus().is_policy || data.GetNpadStatus().is_full_policy) {
        data.SetNpadJoyHoldType(default_hold_type);
    }

    active_data = data;
    if (data.GetNpadStatus().is_hold_type_set) {
        active_data.SetNpadJoyHoldType(default_hold_type);
    }
}

std::size_t NPadResource::GetIndexFromAruid(u64 aruid) const {
    for (std::size_t i = 0; i < AruidIndexMax; i++) {
        if (registration_list.flag[i] == RegistrationStatus::Initialized &&
            registration_list.aruid[i] == aruid) {
            return i;
        }
    }
    return AruidIndexMax;
}

Result NPadResource::ApplyNpadSystemCommonPolicy(u64 aruid, bool is_full_policy) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    auto& data = state[aruid_index].data;
    data.SetNpadSystemCommonPolicy(is_full_policy);
    data.SetNpadJoyHoldType(default_hold_type);
    if (active_data_aruid == aruid) {
        active_data.SetNpadSystemCommonPolicy(is_full_policy);
        active_data.SetNpadJoyHoldType(default_hold_type);
    }
    return ResultSuccess;
}

Result NPadResource::ClearNpadSystemCommonPolicy(u64 aruid) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].data.ClearNpadSystemCommonPolicy();
    if (active_data_aruid == aruid) {
        active_data.ClearNpadSystemCommonPolicy();
    }
    return ResultSuccess;
}

Result NPadResource::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet style_set) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    auto& data = state[aruid_index].data;
    data.SetSupportedNpadStyleSet(style_set);
    if (active_data_aruid == aruid) {
        active_data.SetSupportedNpadStyleSet(style_set);
        active_data.SetNpadJoyHoldType(data.GetNpadJoyHoldType());
    }
    return ResultSuccess;
}

Result NPadResource::GetSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_Set,
                                              u64 aruid) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    auto& data = state[aruid_index].data;
    if (!data.GetNpadStatus().is_supported_styleset_set) {
        return ResultUndefinedStyleset;
    }

    out_style_Set = data.GetSupportedNpadStyleSet();
    return ResultSuccess;
}

Result NPadResource::GetMaskedSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_set,
                                                    u64 aruid) const {
    if (aruid == SystemAruid) {
        out_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
                        Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
                        Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Palma |
                        Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
        return ResultSuccess;
    }

    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    auto& data = state[aruid_index].data;
    if (!data.GetNpadStatus().is_supported_styleset_set) {
        return ResultUndefinedStyleset;
    }

    Core::HID::NpadStyleSet mask{Core::HID::NpadStyleSet::None};
    out_style_set = data.GetSupportedNpadStyleSet();

    switch (state[aruid_index].npad_revision) {
    case NpadRevision::Revision1:
        mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
               Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
               Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
               Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::SystemExt |
               Core::HID::NpadStyleSet::System;
        break;
    case NpadRevision::Revision2:
        mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
               Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
               Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
               Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
               Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
        break;
    case NpadRevision::Revision3:
        mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
               Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
               Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
               Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
               Core::HID::NpadStyleSet::HandheldLark | Core::HID::NpadStyleSet::Lucia |
               Core::HID::NpadStyleSet::Lagoon | Core::HID::NpadStyleSet::Lager |
               Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
        break;
    default:
        mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
               Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
               Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::SystemExt |
               Core::HID::NpadStyleSet::System;
        break;
    }

    out_style_set = out_style_set & mask;
    return ResultSuccess;
}

Result NPadResource::GetAvailableStyleset(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    auto& data = state[aruid_index].data;
    if (!data.GetNpadStatus().is_supported_styleset_set) {
        return ResultUndefinedStyleset;
    }

    Core::HID::NpadStyleSet mask{Core::HID::NpadStyleSet::None};
    out_style_set = data.GetSupportedNpadStyleSet();

    switch (state[aruid_index].npad_revision) {
    case NpadRevision::Revision1:
        mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
               Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
               Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
               Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::SystemExt |
               Core::HID::NpadStyleSet::System;
        break;
    case NpadRevision::Revision2:
        mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
               Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
               Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
               Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
               Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
        break;
    case NpadRevision::Revision3:
        mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
               Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
               Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
               Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
               Core::HID::NpadStyleSet::HandheldLark | Core::HID::NpadStyleSet::Lucia |
               Core::HID::NpadStyleSet::Lagoon | Core::HID::NpadStyleSet::Lager |
               Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
        break;
    default:
        mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
               Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
               Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::SystemExt |
               Core::HID::NpadStyleSet::System;
        break;
    }

    out_style_set = out_style_set & mask;
    return ResultSuccess;
}

NpadRevision NPadResource::GetNpadRevision(u64 aruid) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return NpadRevision::Revision0;
    }

    return state[aruid_index].npad_revision;
}

Result NPadResource::IsSupportedNpadStyleSet(bool& is_set, u64 aruid) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    is_set = state[aruid_index].data.GetNpadStatus().is_supported_styleset_set.Value() != 0;
    return ResultSuccess;
}

Result NPadResource::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].data.SetNpadJoyHoldType(hold_type);
    if (active_data_aruid == aruid) {
        active_data.SetNpadJoyHoldType(hold_type);
    }
    return ResultSuccess;
}

Result NPadResource::GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    auto& data = state[aruid_index].data;
    if (data.GetNpadStatus().is_policy || data.GetNpadStatus().is_full_policy) {
        hold_type = active_data.GetNpadJoyHoldType();
        return ResultSuccess;
    }
    hold_type = data.GetNpadJoyHoldType();
    return ResultSuccess;
}

Result NPadResource::SetNpadHandheldActivationMode(u64 aruid,
                                                   NpadHandheldActivationMode activation_mode) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].data.SetHandheldActivationMode(activation_mode);
    if (active_data_aruid == aruid) {
        active_data.SetHandheldActivationMode(activation_mode);
    }
    return ResultSuccess;
}

Result NPadResource::GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode,
                                                   u64 aruid) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    activation_mode = state[aruid_index].data.GetHandheldActivationMode();
    return ResultSuccess;
}

Result NPadResource::SetSupportedNpadIdType(
    u64 aruid, std::span<const Core::HID::NpadIdType> supported_npad_list) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }
    if (supported_npad_list.size() > MaxSupportedNpadIdTypes) {
        return ResultInvalidArraySize;
    }

    Result result = state[aruid_index].data.SetSupportedNpadIdType(supported_npad_list);
    if (result.IsSuccess() && active_data_aruid == aruid) {
        result = active_data.SetSupportedNpadIdType(supported_npad_list);
    }

    return result;
}

bool NPadResource::IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return false;
    }
    return state[aruid_index].data.IsNpadStyleIndexSupported(style_index);
}

Result NPadResource::SetLrAssignmentMode(u64 aruid, bool is_enabled) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].data.SetLrAssignmentMode(is_enabled);
    if (active_data_aruid == aruid) {
        active_data.SetLrAssignmentMode(is_enabled);
    }
    return ResultSuccess;
}

Result NPadResource::GetLrAssignmentMode(bool& is_enabled, u64 aruid) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    is_enabled = state[aruid_index].data.GetLrAssignmentMode();
    return ResultSuccess;
}

Result NPadResource::SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].data.SetAssigningSingleOnSlSrPress(is_enabled);
    if (active_data_aruid == aruid) {
        active_data.SetAssigningSingleOnSlSrPress(is_enabled);
    }
    return ResultSuccess;
}

Result NPadResource::IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    is_enabled = state[aruid_index].data.GetAssigningSingleOnSlSrPress();
    return ResultSuccess;
}

Result NPadResource::AcquireNpadStyleSetUpdateEventHandle(u64 aruid,
                                                          Kernel::KReadableEvent** out_event,
                                                          Core::HID::NpadIdType npad_id) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    auto& controller_state = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)];
    if (!controller_state.is_styleset_update_event_initialized) {
        // Auto clear = true
        controller_state.style_set_update_event =
            service_context.CreateEvent("NpadResource:StylesetUpdateEvent");

        // Assume creating the event succeeds otherwise crash the system here
        controller_state.is_styleset_update_event_initialized = true;
    }

    *out_event = &controller_state.style_set_update_event->GetReadableEvent();

    if (controller_state.is_styleset_update_event_initialized) {
        controller_state.style_set_update_event->Signal();
    }

    return ResultSuccess;
}

Result NPadResource::SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }
    auto controller = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)];
    if (controller.is_styleset_update_event_initialized) {
        controller.style_set_update_event->Signal();
    }
    return ResultSuccess;
}

Result NPadResource::GetHomeProtectionEnabled(bool& is_enabled, u64 aruid,
                                              Core::HID::NpadIdType npad_id) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    is_enabled = state[aruid_index].data.GetHomeProtectionEnabled(npad_id);
    return ResultSuccess;
}

Result NPadResource::SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id,
                                              bool is_enabled) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].data.SetHomeProtectionEnabled(is_enabled, npad_id);
    if (active_data_aruid == aruid) {
        active_data.SetHomeProtectionEnabled(is_enabled, npad_id);
    }
    return ResultSuccess;
}

Result NPadResource::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].data.SetNpadAnalogStickUseCenterClamp(is_enabled);
    if (active_data_aruid == aruid) {
        active_data.SetNpadAnalogStickUseCenterClamp(is_enabled);
    }
    return ResultSuccess;
}

Result NPadResource::SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index,
                                     Core::HID::NpadButton button_config) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].button_config[NpadIdTypeToIndex(npad_id)][index] = button_config;
    return ResultSuccess;
}

Core::HID::NpadButton NPadResource::GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id,
                                                    std::size_t index, Core::HID::NpadButton mask,
                                                    bool is_enabled) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return Core::HID::NpadButton::None;
    }

    auto& button_config = state[aruid_index].button_config[NpadIdTypeToIndex(npad_id)][index];
    if (is_enabled) {
        button_config = button_config | mask;
        return button_config;
    }

    button_config = Core::HID::NpadButton::None;
    return Core::HID::NpadButton::None;
}

void NPadResource::ResetButtonConfig() {
    for (auto& selected_state : state) {
        selected_state.button_config = {};
    }
}

Result NPadResource::SetNpadCaptureButtonAssignment(u64 aruid,
                                                    Core::HID::NpadStyleSet npad_style_set,
                                                    Core::HID::NpadButton button_assignment) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    // Must be a power of two
    const auto raw_styleset = static_cast<u32>(npad_style_set);
    if (raw_styleset == 0 && (raw_styleset & (raw_styleset - 1)) != 0) {
        return ResultMultipleStyleSetSelected;
    }

    std::size_t style_index{};
    Core::HID::NpadStyleSet style_selected{};
    for (style_index = 0; style_index < StyleIndexCount; ++style_index) {
        style_selected = GetStylesetByIndex(style_index);
        if (npad_style_set == style_selected) {
            break;
        }
    }

    if (style_selected == Core::HID::NpadStyleSet::None) {
        return ResultMultipleStyleSetSelected;
    }

    state[aruid_index].data.SetCaptureButtonAssignment(button_assignment, style_index);
    if (active_data_aruid == aruid) {
        active_data.SetCaptureButtonAssignment(button_assignment, style_index);
    }
    return ResultSuccess;
}

Result NPadResource::ClearNpadCaptureButtonAssignment(u64 aruid) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    for (std::size_t i = 0; i < StyleIndexCount; i++) {
        state[aruid_index].data.SetCaptureButtonAssignment(Core::HID::NpadButton::None, i);
        if (active_data_aruid == aruid) {
            active_data.SetCaptureButtonAssignment(Core::HID::NpadButton::None, i);
        }
    }
    return ResultSuccess;
}

std::size_t NPadResource::GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list,
                                                         u64 aruid) const {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return 0;
    }
    return state[aruid_index].data.GetNpadCaptureButtonAssignmentList(out_list);
}

void NPadResource::SetNpadRevision(u64 aruid, NpadRevision revision) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return;
    }

    state[aruid_index].npad_revision = revision;
}

Result NPadResource::SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index >= AruidIndexMax) {
        return ResultNpadNotConnected;
    }

    state[aruid_index].data.SetNpadAnalogStickUseCenterClamp(is_enabled);
    if (active_data_aruid == aruid) {
        active_data.SetNpadAnalogStickUseCenterClamp(is_enabled);
    }
    return ResultSuccess;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>
#include <mutex>
#include <span>

#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/npad/npad_data.h"
#include "hid_core/resources/npad/npad_types.h"

namespace Core {
class System;
}

namespace Kernel {
class KReadableEvent;
}

namespace Service::HID {
struct DataStatusFlag;

struct NpadControllerState {
    bool is_styleset_update_event_initialized{};
    INSERT_PADDING_BYTES(0x7);
    Kernel::KEvent* style_set_update_event{nullptr};
    INSERT_PADDING_BYTES(0x27);
};

struct NpadState {
    DataStatusFlag flag{};
    u64 aruid{};
    NPadData data{};
    std::array<std::array<Core::HID::NpadButton, StyleIndexCount>, MaxSupportedNpadIdTypes>
        button_config;
    std::array<NpadControllerState, MaxSupportedNpadIdTypes> controller_state;
    NpadRevision npad_revision;
};

/// Handles Npad request from HID interfaces
class NPadResource final {
public:
    explicit NPadResource(KernelHelpers::ServiceContext& context);
    ~NPadResource();

    NPadData* GetActiveData();
    u64 GetActiveDataAruid();

    Result RegisterAppletResourceUserId(u64 aruid);
    void UnregisterAppletResourceUserId(u64 aruid);

    void FreeAppletResourceId(u64 aruid);

    Result Activate(u64 aruid);
    Result Activate();
    Result Deactivate();

    void SetAppletResourceUserId(u64 aruid);
    std::size_t GetIndexFromAruid(u64 aruid) const;

    Result ApplyNpadSystemCommonPolicy(u64 aruid, bool is_full_policy);
    Result ClearNpadSystemCommonPolicy(u64 aruid);

    Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet style_set);
    Result GetSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_Set, u64 aruid) const;
    Result GetMaskedSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const;
    Result GetAvailableStyleset(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const;

    NpadRevision GetNpadRevision(u64 aruid) const;
    void SetNpadRevision(u64 aruid, NpadRevision revision);

    Result IsSupportedNpadStyleSet(bool& is_set, u64 aruid);

    Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type);
    Result GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const;

    Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode activation_mode);
    Result GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode,
                                         u64 aruid) const;

    Result SetSupportedNpadIdType(u64 aruid,
                                  std::span<const Core::HID::NpadIdType> supported_npad_list);
    bool IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const;

    Result SetLrAssignmentMode(u64 aruid, bool is_enabled);
    Result GetLrAssignmentMode(bool& is_enabled, u64 aruid) const;

    Result SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled);
    Result IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const;

    Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
                                                Core::HID::NpadIdType npad_id);
    Result SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id);

    Result GetHomeProtectionEnabled(bool& is_enabled, u64 aruid,
                                    Core::HID::NpadIdType npad_id) const;
    Result SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id, bool is_enabled);

    Result SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled);

    Result SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index,
                           Core::HID::NpadButton button_config);
    Core::HID::NpadButton GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id,
                                          std::size_t index, Core::HID::NpadButton mask,
                                          bool is_enabled);
    void ResetButtonConfig();

    Result SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set,
                                          Core::HID::NpadButton button_assignment);
    Result ClearNpadCaptureButtonAssignment(u64 aruid);
    std::size_t GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list,
                                               u64 aruid) const;

    Result SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled);

private:
    NPadData active_data{};
    AruidRegisterList registration_list{};
    std::array<NpadState, AruidIndexMax> state{};
    u64 active_data_aruid{};
    NpadJoyHoldType default_hold_type{};
    s32 ref_counter{};

    KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "hid_core/hid_types.h"

namespace Core::HID {
class EmulatedController;
}

namespace Service::HID {
static constexpr std::size_t MaxSupportedNpadIdTypes = 10;
static constexpr std::size_t StyleIndexCount = 7;

// 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::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
    INSERT_PADDING_BYTES(0x4);
};

// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
    None = 0,
    HandheldNone = 1,
    HandheldJoyConLeftOnly = 2,
    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,
};

using AppletFooterUiVariant = u8;

// This is "nn::hid::system::AppletDetailedUiType".
struct AppletDetailedUiType {
    AppletFooterUiVariant ui_variant;
    INSERT_PADDING_BYTES(0x2);
    AppletFooterUiType footer;
};
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
// This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
    Mode_5ms = 0,
    Mode_10ms = 1,
    Mode_15ms = 2,
    Default = 3,
};

enum class NpadRevision : u32 {
    Revision0 = 0,
    Revision1 = 1,
    Revision2 = 2,
    Revision3 = 3,
};

// 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::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::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,
};

// nn::hidtypes::FeatureType
struct FeatureType {
    union {
        u64 raw{};
        BitField<0, 1, u64> has_left_analog_stick;
        BitField<1, 1, u64> has_right_analog_stick;
        BitField<2, 1, u64> has_left_joy_six_axis_sensor;
        BitField<3, 1, u64> has_right_joy_six_axis_sensor;
        BitField<4, 1, u64> has_fullkey_joy_six_axis_sensor;
        BitField<5, 1, u64> has_left_lra_vibration_device;
        BitField<6, 1, u64> has_right_lra_vibration_device;
        BitField<7, 1, u64> has_gc_vibration_device;
        BitField<8, 1, u64> has_erm_vibration_device;
        BitField<9, 1, u64> has_left_joy_rail_bus;
        BitField<10, 1, u64> has_right_joy_rail_bus;
        BitField<11, 1, u64> has_internal_bus;
        BitField<12, 1, u64> is_palma;
        BitField<13, 1, u64> has_nfc;
        BitField<14, 1, u64> has_ir_sensor;
        BitField<15, 1, u64> is_analog_stick_calibration_supported;
        BitField<16, 1, u64> is_six_axis_Sensor_user_calibration_supported;
        BitField<17, 1, u64> has_left_right_joy_battery;
        BitField<18, 1, u64> has_fullkey_battery;
        BitField<19, 1, u64> is_disconnect_controller_if_battery_none;
        BitField<20, 1, u64> has_controller_color;
        BitField<21, 1, u64> has_grip_color;
        BitField<22, 1, u64> has_identification_code;
        BitField<23, 1, u64> has_bluetooth_address;
        BitField<24, 1, u64> has_mcu;
        BitField<25, 1, u64> has_notification_led;
        BitField<26, 1, u64> has_directional_buttons;
        BitField<27, 1, u64> has_indicator_led;
        BitField<28, 1, u64> is_button_config_embedded_supported;
        BitField<29, 1, u64> is_button_config_full_supported;
        BitField<30, 1, u64> is_button_config_left_supported;
        BitField<31, 1, u64> is_button_config_right_supported;
        BitField<32, 1, u64> is_usb_hid_device;
        BitField<33, 1, u64> is_kuina_device;
        BitField<34, 1, u64> is_direct_usb_to_bt_switching_device;
        BitField<35, 1, u64> is_normalize_analog_stick_with_inner_cross;
    };
};
static_assert(sizeof(FeatureType) == 8, "FeatureType is an invalid size");

// This is nn::hid::AssignmentStyle
struct AssignmentStyle {
    union {
        u32 raw{};
        BitField<0, 1, u32> is_external_assigned;
        BitField<1, 1, u32> is_external_left_assigned;
        BitField<2, 1, u32> is_external_right_assigned;
        BitField<3, 1, u32> is_handheld_assigned;
        BitField<4, 1, u32> is_handheld_left_assigned;
        BitField<5, 1, u32> is_handheld_right_assigned;
    };
};
static_assert(sizeof(AssignmentStyle) == 4, "AssignmentStyle is an invalid size");

// This is nn::hid::server::IAbstractedPad::InternalFlags
struct InternalFlags {
    union {
        u32 raw{};
        BitField<0, 1, u32> is_bound;
        BitField<1, 1, u32> is_connected;
        BitField<2, 1, u32> is_battery_low_ovln_required;
        BitField<3, 1, u32> is_battery_low_ovln_delay_required;
        BitField<4, 1, u32> is_sample_received;
        BitField<5, 1, u32> is_virtual_input;
        BitField<6, 1, u32> is_wired;
        BitField<8, 1, u32> use_center_clamp;
        BitField<9, 1, u32> has_virtual_six_axis_sensor_acceleration;
        BitField<10, 1, u32> has_virtual_six_axis_sensor_angle;
        BitField<11, 1, u32> is_debug_pad;
    };
};
static_assert(sizeof(InternalFlags) == 4, "InternalFlags is an invalid size");

/// This is nn::hid::server::IAbstractedPad
struct IAbstractedPad {
    InternalFlags internal_flags;
    u64 controller_id;
    u32 controller_number;
    u64 low_battery_display_delay_time;
    u64 low_battery_display_delay_interval;
    FeatureType feature_set;
    FeatureType disabled_feature_set;
    AssignmentStyle assignment_style;
    Core::HID::NpadStyleIndex device_type;
    Core::HID::NpadInterfaceType interface_type;
    Core::HID::NpadPowerInfo power_info;
    u32 pad_state;
    u32 button_mask;
    u32 system_button_mask;
    u8 indicator;
    std::vector<f32> virtual_six_axis_sensor_acceleration;
    std::vector<f32> virtual_six_axis_sensor_angle;
    Core::HID::EmulatedController* xcd_handle;
    u64 color;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "core/hle/service/set/system_settings_server.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/npad/npad_vibration.h"

namespace Service::HID {

NpadVibration::NpadVibration() {}

NpadVibration::~NpadVibration() = default;

Result NpadVibration::Activate() {
    std::scoped_lock lock{mutex};

    f32 master_volume = 1.0f;
    m_set_sys->GetVibrationMasterVolume(&master_volume);
    if (master_volume < 0.0f || master_volume > 1.0f) {
        return ResultVibrationStrengthOutOfRange;
    }

    volume = master_volume;
    return ResultSuccess;
}

Result NpadVibration::Deactivate() {
    return ResultSuccess;
}

Result NpadVibration::SetSettingsService(
    std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
    m_set_sys = settings;
    return ResultSuccess;
}

Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) {
    std::scoped_lock lock{mutex};

    if (master_volume < 0.0f && master_volume > 1.0f) {
        return ResultVibrationStrengthOutOfRange;
    }

    volume = master_volume;
    m_set_sys->SetVibrationMasterVolume(master_volume);

    return ResultSuccess;
}

Result NpadVibration::GetVibrationVolume(f32& out_volume) const {
    std::scoped_lock lock{mutex};
    out_volume = volume;
    return ResultSuccess;
}

Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const {
    std::scoped_lock lock{mutex};

    f32 master_volume = 1.0f;
    m_set_sys->GetVibrationMasterVolume(&master_volume);
    if (master_volume < 0.0f || master_volume > 1.0f) {
        return ResultVibrationStrengthOutOfRange;
    }

    out_volume = master_volume;
    return ResultSuccess;
}

Result NpadVibration::BeginPermitVibrationSession(u64 aruid) {
    std::scoped_lock lock{mutex};
    session_aruid = aruid;
    volume = 1.0;
    return ResultSuccess;
}

Result NpadVibration::EndPermitVibrationSession() {
    std::scoped_lock lock{mutex};

    f32 master_volume = 1.0f;
    m_set_sys->GetVibrationMasterVolume(&master_volume);
    if (master_volume < 0.0f || master_volume > 1.0f) {
        return ResultVibrationStrengthOutOfRange;
    }

    volume = master_volume;
    session_aruid = 0;
    return ResultSuccess;
}

u64 NpadVibration::GetSessionAruid() const {
    return session_aruid;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <mutex>

#include "common/common_types.h"
#include "core/hle/result.h"

namespace Service::Set {
class ISystemSettingsServer;
}

namespace Service::HID {

class NpadVibration final {
public:
    explicit NpadVibration();
    ~NpadVibration();

    Result Activate();
    Result Deactivate();

    Result SetSettingsService(std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
    Result SetVibrationMasterVolume(f32 master_volume);
    Result GetVibrationVolume(f32& out_volume) const;
    Result GetVibrationMasterVolume(f32& out_volume) const;

    Result BeginPermitVibrationSession(u64 aruid);
    Result EndPermitVibrationSession();

    u64 GetSessionAruid() const;

private:
    f32 volume{};
    u64 session_aruid{};
    mutable std::mutex mutex;

    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core_timing.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/palma/palma.h"

namespace Service::HID {

Palma::Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
    : ControllerBase{hid_core_}, service_context{service_context_} {
    controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
    operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
}

Palma::~Palma() {
    service_context.CloseEvent(operation_complete_event);
};

void Palma::OnInit() {}

void Palma::OnRelease() {}

void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    if (!IsControllerActivated()) {
        return;
    }
}

Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
                                       PalmaConnectionHandle& handle) {
    active_handle.npad_id = npad_id;
    handle = active_handle;
    return ResultSuccess;
}

Result Palma::InitializePalma(const PalmaConnectionHandle& handle) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    Activate();
    return ResultSuccess;
}

Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent(
    const PalmaConnectionHandle& handle) const {
    if (handle.npad_id != active_handle.npad_id) {
        LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
    }
    return operation_complete_event->GetReadableEvent();
}

Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
                                    PalmaOperationType& operation_type,
                                    std::span<u8> out_data) const {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    operation_type = static_cast<PalmaOperationType>(operation.operation);
    std::memcpy(out_data.data(), operation.data.data(),
                std::min(out_data.size(), operation.data.size()));

    return ResultSuccess;
}

Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    operation.operation = PackedPalmaOperationType::PlayActivity;
    operation.result = PalmaResultSuccess;
    operation.data = {};
    operation_complete_event->Signal();
    return ResultSuccess;
}

Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    fr_mode = fr_mode_;
    return ResultSuccess;
}

Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    operation.operation = PackedPalmaOperationType::ReadStep;
    operation.result = PalmaResultSuccess;
    operation.data = {};
    operation_complete_event->Signal();
    return ResultSuccess;
}

Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    return ResultSuccess;
}

Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    return ResultSuccess;
}

void Palma::ReadPalmaApplicationSection() {}

void Palma::WritePalmaApplicationSection() {}

Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    operation.operation = PackedPalmaOperationType::ReadUniqueCode;
    operation.result = PalmaResultSuccess;
    operation.data = {};
    operation_complete_event->Signal();
    return ResultSuccess;
}

Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    operation.operation = PackedPalmaOperationType::SetUniqueCodeInvalid;
    operation.result = PalmaResultSuccess;
    operation.data = {};
    operation_complete_event->Signal();
    return ResultSuccess;
}

void Palma::WritePalmaActivityEntry() {}

Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    operation.operation = PackedPalmaOperationType::WriteRgbLedPatternEntry;
    operation.result = PalmaResultSuccess;
    operation.data = {};
    operation_complete_event->Signal();
    return ResultSuccess;
}

Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
                                  Common::ProcessAddress t_mem, u64 size) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    operation.operation = PackedPalmaOperationType::WriteWaveEntry;
    operation.result = PalmaResultSuccess;
    operation.data = {};
    operation_complete_event->Signal();
    return ResultSuccess;
}

Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
                                                    s32 database_id_version_) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    database_id_version = database_id_version_;
    operation.operation = PackedPalmaOperationType::ReadDataBaseIdentificationVersion;
    operation.result = PalmaResultSuccess;
    operation.data[0] = {};
    operation_complete_event->Signal();
    return ResultSuccess;
}

Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    operation.operation = PackedPalmaOperationType::ReadDataBaseIdentificationVersion;
    operation.result = PalmaResultSuccess;
    operation.data = {};
    operation.data[0] = static_cast<u8>(database_id_version);
    operation_complete_event->Signal();
    return ResultSuccess;
}

void Palma::SuspendPalmaFeature() {}

Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    return operation.result;
}
void Palma::ReadPalmaPlayLog() {}

void Palma::ResetPalmaPlayLog() {}

void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
    // If true controllers are able to be paired
    is_connectable = is_all_connectable;
}

void Palma::SetIsPalmaPairedConnectable() {}

Result Palma::PairPalma(const PalmaConnectionHandle& handle) {
    if (handle.npad_id != active_handle.npad_id) {
        return InvalidPalmaHandle;
    }
    // TODO: Do something
    return ResultSuccess;
}

void Palma::SetPalmaBoostMode(bool boost_mode) {}

void Palma::CancelWritePalmaWaveEntry() {}

void Palma::EnablePalmaBoostMode() {}

void Palma::GetPalmaBluetoothAddress() {}

void Palma::SetDisallowedPalmaConnection() {}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <span>

#include "common/common_funcs.h"
#include "common/typed_address.h"
#include "hid_core/hid_result.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/controller_base.h"

namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel

namespace Service::KernelHelpers {
class ServiceContext;
}

namespace Core::HID {
class EmulatedController;
} // namespace Core::HID

namespace Service::HID {
class Palma final : public ControllerBase {
public:
    using PalmaOperationData = std::array<u8, 0x140>;
    using PalmaApplicationSection = std::array<u8, 0x100>;
    using Address = std::array<u8, 0x6>;

    // This is nn::hid::PalmaOperationType
    enum class PalmaOperationType : u64 {
        PlayActivity,
        SetFrModeType,
        ReadStep,
        EnableStep,
        ResetStep,
        ReadApplicationSection,
        WriteApplicationSection,
        ReadUniqueCode,
        SetUniqueCodeInvalid,
        WriteActivityEntry,
        WriteRgbLedPatternEntry,
        WriteWaveEntry,
        ReadDataBaseIdentificationVersion,
        WriteDataBaseIdentificationVersion,
        SuspendFeature,
        ReadPlayLog,
        ResetPlayLog,
    };

    enum class PackedPalmaOperationType : u32 {
        PlayActivity,
        SetFrModeType,
        ReadStep,
        EnableStep,
        ResetStep,
        ReadApplicationSection,
        WriteApplicationSection,
        ReadUniqueCode,
        SetUniqueCodeInvalid,
        WriteActivityEntry,
        WriteRgbLedPatternEntry,
        WriteWaveEntry,
        ReadDataBaseIdentificationVersion,
        WriteDataBaseIdentificationVersion,
        SuspendFeature,
        ReadPlayLog,
        ResetPlayLog,
    };

    // This is nn::hid::PalmaWaveSet
    enum class PalmaWaveSet : u64 {
        Small,
        Medium,
        Large,
    };

    // This is nn::hid::PalmaFrModeType
    enum class PalmaFrModeType : u64 {
        Off,
        B01,
        B02,
        B03,
        Downloaded,
    };

    // This is nn::hid::PalmaFeature
    enum class PalmaFeature : u64 {
        FrMode,
        RumbleFeedback,
        Step,
        MuteSwitch,
    };

    // This is nn::hid::PalmaOperationInfo
    struct PalmaOperationInfo {
        PackedPalmaOperationType operation{};
        Result result{PalmaResultSuccess};
        PalmaOperationData data{};
    };
    static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size");

    // This is nn::hid::PalmaActivityEntry
    struct PalmaActivityEntry {
        u32 rgb_led_pattern_index;
        INSERT_PADDING_BYTES(2);
        PalmaWaveSet wave_set;
        u32 wave_index;
        INSERT_PADDING_BYTES(12);
    };
    static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size");

    struct PalmaConnectionHandle {
        alignas(8) Core::HID::NpadIdType npad_id;
    };
    static_assert(sizeof(PalmaConnectionHandle) == 0x8,
                  "PalmaConnectionHandle has incorrect size.");

    explicit Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_);
    ~Palma() 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;

    Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle);
    Result InitializePalma(const PalmaConnectionHandle& handle);
    Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent(
        const PalmaConnectionHandle& handle) const;
    Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
                                 PalmaOperationType& operation_type, std::span<u8> out_data) const;
    Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity);
    Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_);
    Result ReadPalmaStep(const PalmaConnectionHandle& handle);
    Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled);
    Result ResetPalmaStep(const PalmaConnectionHandle& handle);
    Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle);
    Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle);
    Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown);
    Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
                               Common::ProcessAddress t_mem, u64 size);
    Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
                                                 s32 database_id_version_);
    Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle);
    Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const;
    void SetIsPalmaAllConnectable(bool is_all_connectable);
    Result PairPalma(const PalmaConnectionHandle& handle);
    void SetPalmaBoostMode(bool boost_mode);

private:
    void ReadPalmaApplicationSection();
    void WritePalmaApplicationSection();
    void WritePalmaActivityEntry();
    void SuspendPalmaFeature();
    void ReadPalmaPlayLog();
    void ResetPalmaPlayLog();
    void SetIsPalmaPairedConnectable();
    void CancelWritePalmaWaveEntry();
    void EnablePalmaBoostMode();
    void GetPalmaBluetoothAddress();
    void SetDisallowedPalmaConnection();

    bool is_connectable{};
    s32 database_id_version{};
    PalmaOperationInfo operation{};
    PalmaFrModeType fr_mode{};
    PalmaConnectionHandle active_handle{};

    Core::HID::EmulatedController* controller;

    Kernel::KEvent* operation_complete_event;
    KernelHelpers::ServiceContext& service_context;
};

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#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 + max_buffer_size - 1) % max_buffer_size);
    }

    std::size_t GetNextEntryIndex() const {
        return static_cast<size_t>((buffer_tail + 1) % max_buffer_size);
    }

    void WriteNextEntry(const State& new_state) {
        if (buffer_count < static_cast<s64>(max_buffer_size) - 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


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/vector_math.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/debug_pad/debug_pad_types.h"
#include "hid_core/resources/keyboard/keyboard_types.h"
#include "hid_core/resources/mouse/mouse_types.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/ring_lifo.h"
#include "hid_core/resources/system_buttons/system_button_types.h"
#include "hid_core/resources/touch_screen/touch_types.h"

namespace Service::HID {
static const std::size_t HidEntryCount = 17;

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");

// This is nn::hid::detail::DebugPadSharedMemoryFormat
struct DebugPadSharedMemoryFormat {
    // This is nn::hid::detail::DebugPadLifo
    Lifo<DebugPadState, HidEntryCount> debug_pad_lifo{};
    static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
    INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(DebugPadSharedMemoryFormat) == 0x400,
              "DebugPadSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::TouchScreenSharedMemoryFormat
struct TouchScreenSharedMemoryFormat {
    // This is nn::hid::detail::TouchScreenLifo
    Lifo<TouchScreenState, HidEntryCount> touch_screen_lifo{};
    static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
    INSERT_PADDING_WORDS(0xF2);
};
static_assert(sizeof(TouchScreenSharedMemoryFormat) == 0x3000,
              "TouchScreenSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::MouseSharedMemoryFormat
struct MouseSharedMemoryFormat {
    // This is nn::hid::detail::MouseLifo
    Lifo<Core::HID::MouseState, HidEntryCount> mouse_lifo{};
    static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
    INSERT_PADDING_WORDS(0x2C);
};
static_assert(sizeof(MouseSharedMemoryFormat) == 0x400,
              "MouseSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::KeyboardSharedMemoryFormat
struct KeyboardSharedMemoryFormat {
    // This is nn::hid::detail::KeyboardLifo
    Lifo<KeyboardState, HidEntryCount> keyboard_lifo{};
    static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
    INSERT_PADDING_WORDS(0xA);
};
static_assert(sizeof(KeyboardSharedMemoryFormat) == 0x400,
              "KeyboardSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::DigitizerSharedMemoryFormat
struct DigitizerSharedMemoryFormat {
    CommonHeader header;
    INSERT_PADDING_BYTES(0xFE0);
};
static_assert(sizeof(DigitizerSharedMemoryFormat) == 0x1000,
              "DigitizerSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::HomeButtonSharedMemoryFormat
struct HomeButtonSharedMemoryFormat {
    Lifo<HomeButtonState, HidEntryCount> home_lifo{};
    INSERT_PADDING_BYTES(0x48);
};
static_assert(sizeof(HomeButtonSharedMemoryFormat) == 0x200,
              "HomeButtonSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::SleepButtonSharedMemoryFormat
struct SleepButtonSharedMemoryFormat {
    Lifo<SleepButtonState, HidEntryCount> sleep_lifo{};
    INSERT_PADDING_BYTES(0x48);
};
static_assert(sizeof(SleepButtonSharedMemoryFormat) == 0x200,
              "SleepButtonSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::CaptureButtonSharedMemoryFormat
struct CaptureButtonSharedMemoryFormat {
    Lifo<CaptureButtonState, HidEntryCount> capture_lifo{};
    INSERT_PADDING_BYTES(0x48);
};
static_assert(sizeof(CaptureButtonSharedMemoryFormat) == 0x200,
              "CaptureButtonSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::InputDetectorSharedMemoryFormat
struct InputDetectorSharedMemoryFormat {
    CommonHeader header;
    INSERT_PADDING_BYTES(0x7E0);
};
static_assert(sizeof(InputDetectorSharedMemoryFormat) == 0x800,
              "InputDetectorSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::UniquePadSharedMemoryFormat
struct UniquePadSharedMemoryFormat {
    CommonHeader header;
    INSERT_PADDING_BYTES(0x3FE0);
};
static_assert(sizeof(UniquePadSharedMemoryFormat) == 0x4000,
              "UniquePadSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::NpadSixAxisSensorLifo
struct NpadSixAxisSensorLifo {
    Lifo<Core::HID::SixAxisSensorState, HidEntryCount> 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<NPadGenericState, HidEntryCount> fullkey_lifo{};
    Lifo<NPadGenericState, HidEntryCount> handheld_lifo{};
    Lifo<NPadGenericState, HidEntryCount> joy_dual_lifo{};
    Lifo<NPadGenericState, HidEntryCount> joy_left_lifo{};
    Lifo<NPadGenericState, HidEntryCount> joy_right_lifo{};
    Lifo<NPadGenericState, HidEntryCount> palma_lifo{};
    Lifo<NPadGenericState, HidEntryCount> system_ext_lifo{};
    NpadSixAxisSensorLifo sixaxis_fullkey_lifo{};
    NpadSixAxisSensorLifo sixaxis_handheld_lifo{};
    NpadSixAxisSensorLifo sixaxis_dual_left_lifo{};
    NpadSixAxisSensorLifo sixaxis_dual_right_lifo{};
    NpadSixAxisSensorLifo sixaxis_left_lifo{};
    NpadSixAxisSensorLifo 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{};
    AppletFooterUiAttributes applet_footer_attributes{};
    AppletFooterUiType applet_footer_type{AppletFooterUiType::None};
    INSERT_PADDING_BYTES(0x5B); // Reserved
    INSERT_PADDING_BYTES(0x20); // Unknown
    Lifo<NpadGcTriggerState, HidEntryCount> gc_trigger_lifo{};
    NpadLarkType lark_type_l_and_main{};
    NpadLarkType lark_type_r{};
    NpadLuciaType lucia_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;
};
static_assert(sizeof(NpadInternalState) == 0x43F8, "NpadInternalState is an invalid size");

// This is nn::hid::detail::NpadSharedMemoryEntry
struct NpadSharedMemoryEntry {
    NpadInternalState internal_state;
    INSERT_PADDING_BYTES(0xC08);
};
static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is an invalid size");

// This is nn::hid::detail::NpadSharedMemoryFormat
struct NpadSharedMemoryFormat {
    std::array<NpadSharedMemoryEntry, MaxSupportedNpadIdTypes> npad_entry;
};
static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000,
              "NpadSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::GestureSharedMemoryFormat
struct GestureSharedMemoryFormat {
    // This is nn::hid::detail::GestureLifo
    Lifo<GestureState, HidEntryCount> gesture_lifo{};
    static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
    INSERT_PADDING_WORDS(0x3E);
};
static_assert(sizeof(GestureSharedMemoryFormat) == 0x800,
              "GestureSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSixAxisSensorSharedMemoryFormat {
    u64 sampling_number{};
    bool is_seven_six_axis_sensor_at_rest{};
    INSERT_PADDING_BYTES(3); // padding
    f32 verticalization_error{};
    Common::Vec3f gyro_bias{};
    INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
              "ConsoleSixAxisSensorSharedMemoryFormat is an invalid size");

// This is nn::hid::detail::SharedMemoryFormat
struct SharedMemoryFormat {
    void Initialize() {}

    DebugPadSharedMemoryFormat debug_pad;
    TouchScreenSharedMemoryFormat touch_screen;
    MouseSharedMemoryFormat mouse;
    KeyboardSharedMemoryFormat keyboard;
    DigitizerSharedMemoryFormat digitizer;
    HomeButtonSharedMemoryFormat home_button;
    SleepButtonSharedMemoryFormat sleep_button;
    CaptureButtonSharedMemoryFormat capture_button;
    InputDetectorSharedMemoryFormat input_detector;
    UniquePadSharedMemoryFormat unique_pad;
    NpadSharedMemoryFormat npad;
    GestureSharedMemoryFormat gesture;
    ConsoleSixAxisSensorSharedMemoryFormat console;
    INSERT_PADDING_BYTES(0x19E0);
    MouseSharedMemoryFormat debug_mouse;
    INSERT_PADDING_BYTES(0x2000);
};
static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset");
static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset");
static_assert(offsetof(SharedMemoryFormat, mouse) == 0x3400, "mouse has wrong offset");
static_assert(offsetof(SharedMemoryFormat, keyboard) == 0x3800, "keyboard has wrong offset");
static_assert(offsetof(SharedMemoryFormat, digitizer) == 0x3C00, "digitizer has wrong offset");
static_assert(offsetof(SharedMemoryFormat, home_button) == 0x4C00, "home_button has wrong offset");
static_assert(offsetof(SharedMemoryFormat, sleep_button) == 0x4E00,
              "sleep_button has wrong offset");
static_assert(offsetof(SharedMemoryFormat, capture_button) == 0x5000,
              "capture_button has wrong offset");
static_assert(offsetof(SharedMemoryFormat, input_detector) == 0x5200,
              "input_detector has wrong offset");
static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offset");
static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset");
static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset");
static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset");
static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size");

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/shared_memory_holder.h"

namespace Service::HID {
SharedMemoryHolder::SharedMemoryHolder() {}

SharedMemoryHolder::~SharedMemoryHolder() {
    Finalize();
}

Result SharedMemoryHolder::Initialize(Core::System& system) {
    shared_memory = Kernel::KSharedMemory::Create(system.Kernel());
    const Result result = shared_memory->Initialize(
        system.DeviceMemory(), nullptr, Kernel::Svc::MemoryPermission::None,
        Kernel::Svc::MemoryPermission::Read, sizeof(SharedMemoryFormat));
    if (result.IsError()) {
        return result;
    }
    Kernel::KSharedMemory::Register(system.Kernel(), shared_memory);

    is_created = true;
    is_mapped = true;
    address = std::construct_at(reinterpret_cast<SharedMemoryFormat*>(shared_memory->GetPointer()));
    return ResultSuccess;
}

void SharedMemoryHolder::Finalize() {
    if (address != nullptr) {
        shared_memory->Close();
    }
    is_created = false;
    is_mapped = false;
    address = nullptr;
}

bool SharedMemoryHolder::IsMapped() {
    return is_mapped;
}

SharedMemoryFormat* SharedMemoryHolder::GetAddress() {
    return address;
}

Kernel::KSharedMemory* SharedMemoryHolder::GetHandle() {
    return shared_memory;
}
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Core {
class System;
}

namespace Kernel {
class KSharedMemory;
}

namespace Service::HID {
struct SharedMemoryFormat;

// This is nn::hid::detail::SharedMemoryHolder
class SharedMemoryHolder {
public:
    SharedMemoryHolder();
    ~SharedMemoryHolder();

    Result Initialize(Core::System& system);
    void Finalize();

    bool IsMapped();
    SharedMemoryFormat* GetAddress();
    Kernel::KSharedMemory* GetHandle();

private:
    bool is_owner{};
    bool is_created{};
    bool is_mapped{};
    INSERT_PADDING_BYTES(0x5);
    Kernel::KSharedMemory* shared_memory;
    INSERT_PADDING_BYTES(0x38);
    SharedMemoryFormat* address = nullptr;
};
// Correct size is 0x50 bytes
static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size");

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core_timing.h"
#include "hid_core/frontend/emulated_console.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/six_axis/console_six_axis.h"

namespace Service::HID {

ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
    console = hid_core.GetEmulatedConsole();
}

ConsoleSixAxis::~ConsoleSixAxis() = default;

void ConsoleSixAxis::OnInit() {}

void ConsoleSixAxis::OnRelease() {}

void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    std::scoped_lock shared_lock{*shared_mutex};
    const u64 aruid = applet_resource->GetActiveAruid();
    auto* data = applet_resource->GetAruidData(aruid);

    if (data == nullptr || !data->flag.is_assigned) {
        return;
    }

    ConsoleSixAxisSensorSharedMemoryFormat& shared_memory = data->shared_memory_format->console;

    if (!IsControllerActivated()) {
        return;
    }

    const auto motion_status = console->GetMotion();

    shared_memory.sampling_number++;
    shared_memory.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
    shared_memory.verticalization_error = motion_status.verticalization_error;
    shared_memory.gyro_bias = motion_status.gyro_bias;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "hid_core/resources/controller_base.h"

namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID

namespace Service::HID {
class ConsoleSixAxis final : public ControllerBase {
public:
    explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
    ~ConsoleSixAxis() 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;

private:
    Core::HID::EmulatedConsole* console = nullptr;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include <cstring>
#include "common/common_types.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
#include "hid_core/frontend/emulated_console.h"
#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/six_axis/seven_six_axis.h"

namespace Service::HID {
SevenSixAxis::SevenSixAxis(Core::System& system_)
    : ControllerBase{system_.HIDCore()}, system{system_} {
    console = hid_core.GetEmulatedConsole();
}

SevenSixAxis::~SevenSixAxis() = default;

void SevenSixAxis::OnInit() {}
void SevenSixAxis::OnRelease() {}

void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    if (!IsControllerActivated() || transfer_memory == 0) {
        seven_sixaxis_lifo.buffer_count = 0;
        seven_sixaxis_lifo.buffer_tail = 0;
        return;
    }

    const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
    next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;

    const auto motion_status = console->GetMotion();
    last_global_timestamp = core_timing.GetGlobalTimeNs().count();

    // This value increments every time the switch goes to sleep
    next_seven_sixaxis_state.unknown = 1;
    next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp;
    next_seven_sixaxis_state.accel = motion_status.accel;
    next_seven_sixaxis_state.gyro = motion_status.gyro;
    next_seven_sixaxis_state.quaternion = {
        {
            motion_status.quaternion.xyz.y,
            motion_status.quaternion.xyz.x,
            -motion_status.quaternion.w,
        },
        -motion_status.quaternion.xyz.z,
    };

    seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
    system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
                                          sizeof(seven_sixaxis_lifo));
}

void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
    transfer_memory = t_mem;
}

void SevenSixAxis::ResetTimestamp() {
    last_saved_timestamp = last_global_timestamp;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/common_types.h"
#include "common/quaternion.h"
#include "common/typed_address.h"
#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/ring_lifo.h"

namespace Core {
class System;
} // namespace Core

namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID

namespace Service::HID {
class SevenSixAxis final : public ControllerBase {
public:
    explicit SevenSixAxis(Core::System& system_);
    ~SevenSixAxis() 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;

    // Called on InitializeSevenSixAxisSensor
    void SetTransferMemoryAddress(Common::ProcessAddress t_mem);

    // Called on ResetSevenSixAxisSensorTimestamp
    void ResetTimestamp();

private:
    struct SevenSixAxisState {
        INSERT_PADDING_WORDS(2); // unused
        u64 timestamp{};
        u64 sampling_number{};
        u64 unknown{};
        Common::Vec3f accel{};
        Common::Vec3f gyro{};
        Common::Quaternion<f32> quaternion{};
    };
    static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");

    Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
    static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");

    u64 last_saved_timestamp{};
    u64 last_global_timestamp{};

    SevenSixAxisState next_seven_sixaxis_state{};
    Common::ProcessAddress transfer_memory{};
    Core::HID::EmulatedConsole* console = nullptr;

    Core::System& system;
};
} // namespace Service::HID