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

#include "common/common_types.h"
#include "core/core_timing.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_result.h"
#include "hid_core/hid_util.h"
#include "hid_core/resources/npad/npad.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/six_axis/six_axis.h"

namespace Service::HID {

SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_)
    : ControllerBase{hid_core_}, npad{npad_} {
    for (std::size_t i = 0; i < controller_data.size(); ++i) {
        auto& controller = controller_data[i];
        controller.device = hid_core.GetEmulatedControllerByIndex(i);
    }
}

SixAxis::~SixAxis() = default;

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

void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    std::scoped_lock shared_lock{*shared_mutex};

    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
        const auto* data = applet_resource->GetAruidDataByIndex(aruid_index);

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

        if (!IsControllerActivated()) {
            return;
        }

        for (std::size_t i = 0; i < controller_data.size(); ++i) {
            NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
            auto& controller = controller_data[i];
            const auto& controller_type = controller.device->GetNpadStyleIndex();

            if (!data->flag.enable_six_axis_sensor) {
                continue;
            }

            if (controller_type == Core::HID::NpadStyleIndex::None ||
                !controller.device->IsConnected()) {
                continue;
            }

            const auto& motion_state = controller.device->GetMotions();
            auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
            auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
            auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
            auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
            auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
            auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;

            auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
            auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
            auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
            auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
            auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
            auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;

            // Clear previous state
            sixaxis_fullkey_state = {};
            sixaxis_handheld_state = {};
            sixaxis_dual_left_state = {};
            sixaxis_dual_right_state = {};
            sixaxis_left_lifo_state = {};
            sixaxis_right_lifo_state = {};

            if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
                controller.sixaxis_at_rest = true;
                for (std::size_t e = 0; e < motion_state.size(); ++e) {
                    controller.sixaxis_at_rest =
                        controller.sixaxis_at_rest && motion_state[e].is_at_rest;
                }
            }

            const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
                                              const Core::HID::ControllerMotion& hid_state) {
                using namespace std::literals::chrono_literals;
                static constexpr Core::HID::SixAxisSensorState default_motion_state = {
                    .delta_time = std::chrono::nanoseconds(5ms).count(),
                    .accel = {0, 0, -1.0f},
                    .orientation =
                        {
                            Common::Vec3f{1.0f, 0, 0},
                            Common::Vec3f{0, 1.0f, 0},
                            Common::Vec3f{0, 0, 1.0f},
                        },
                    .attribute = {1},
                };
                if (!controller.sixaxis_sensor_enabled) {
                    state = default_motion_state;
                    return;
                }
                if (!Settings::values.motion_enabled.GetValue()) {
                    state = default_motion_state;
                    return;
                }
                state.attribute.is_connected.Assign(1);
                state.delta_time = std::chrono::nanoseconds(5ms).count();
                state.accel = hid_state.accel;
                state.gyro = hid_state.gyro;
                state.rotation = hid_state.rotation;
                state.orientation = hid_state.orientation;
            };

            switch (controller_type) {
            case Core::HID::NpadStyleIndex::None:
                ASSERT(false);
                break;
            case Core::HID::NpadStyleIndex::Fullkey:
                set_motion_state(sixaxis_fullkey_state, motion_state[0]);
                break;
            case Core::HID::NpadStyleIndex::Handheld:
                set_motion_state(sixaxis_handheld_state, motion_state[0]);
                break;
            case Core::HID::NpadStyleIndex::JoyconDual:
                set_motion_state(sixaxis_dual_left_state, motion_state[0]);
                set_motion_state(sixaxis_dual_right_state, motion_state[1]);
                break;
            case Core::HID::NpadStyleIndex::JoyconLeft:
                set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
                break;
            case Core::HID::NpadStyleIndex::JoyconRight:
                set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
                break;
            case Core::HID::NpadStyleIndex::Pokeball:
                using namespace std::literals::chrono_literals;
                set_motion_state(sixaxis_fullkey_state, motion_state[0]);
                sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
                break;
            default:
                break;
            }

            sixaxis_fullkey_state.sampling_number =
                sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
            sixaxis_handheld_state.sampling_number =
                sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
            sixaxis_dual_left_state.sampling_number =
                sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
            sixaxis_dual_right_state.sampling_number =
                sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
            sixaxis_left_lifo_state.sampling_number =
                sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
            sixaxis_right_lifo_state.sampling_number =
                sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;

            if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
                // This buffer only is updated on handheld on HW
                sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
            } else {
                // Handheld doesn't update this buffer on HW
                sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
            }

            sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
            sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
            sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
            sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
        }
    }
}

Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                          Core::HID::GyroscopeZeroDriftMode drift_mode) {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    auto& sixaxis = GetSixaxisState(sixaxis_handle);
    auto& controller = GetControllerFromHandle(sixaxis_handle);
    sixaxis.gyroscope_zero_drift_mode = drift_mode;
    controller.device->SetGyroscopeZeroDriftMode(drift_mode);

    return ResultSuccess;
}

Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                          Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    const auto& sixaxis = GetSixaxisState(sixaxis_handle);
    drift_mode = sixaxis.gyroscope_zero_drift_mode;

    return ResultSuccess;
}

Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                      bool& is_at_rest) const {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    const auto& controller = GetControllerFromHandle(sixaxis_handle);
    is_at_rest = controller.sixaxis_at_rest;
    return ResultSuccess;
}

Result SixAxis::LoadSixAxisSensorCalibrationParameter(
    const Core::HID::SixAxisSensorHandle& sixaxis_handle,
    Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    // TODO: Request this data to the controller. On error return 0xd8ca
    const auto& sixaxis = GetSixaxisState(sixaxis_handle);
    calibration = sixaxis.calibration;
    return ResultSuccess;
}

Result SixAxis::GetSixAxisSensorIcInformation(
    const Core::HID::SixAxisSensorHandle& sixaxis_handle,
    Core::HID::SixAxisSensorIcInformation& ic_information) const {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    // TODO: Request this data to the controller. On error return 0xd8ca
    const auto& sixaxis = GetSixaxisState(sixaxis_handle);
    ic_information = sixaxis.ic_information;
    return ResultSuccess;
}

Result SixAxis::EnableSixAxisSensorUnalteredPassthrough(
    const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    auto& sixaxis = GetSixaxisState(sixaxis_handle);
    sixaxis.unaltered_passthrough = is_enabled;
    return ResultSuccess;
}

Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled(
    const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    const auto& sixaxis = GetSixaxisState(sixaxis_handle);
    is_enabled = sixaxis.unaltered_passthrough;
    return ResultSuccess;
}

Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                  bool sixaxis_status) {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    auto& controller = GetControllerFromHandle(sixaxis_handle);
    controller.sixaxis_sensor_enabled = sixaxis_status;
    return ResultSuccess;
}

Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                             bool& is_fusion_enabled) const {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    const auto& sixaxis = GetSixaxisState(sixaxis_handle);
    is_fusion_enabled = sixaxis.is_fusion_enabled;

    return ResultSuccess;
}
Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                        bool is_fusion_enabled) {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    auto& sixaxis = GetSixaxisState(sixaxis_handle);
    sixaxis.is_fusion_enabled = is_fusion_enabled;

    return ResultSuccess;
}

Result SixAxis::SetSixAxisFusionParameters(
    const Core::HID::SixAxisSensorHandle& sixaxis_handle,
    Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    const auto param1 = sixaxis_fusion_parameters.parameter1;
    if (param1 < 0.0f || param1 > 1.0f) {
        return InvalidSixAxisFusionRange;
    }

    auto& sixaxis = GetSixaxisState(sixaxis_handle);
    sixaxis.fusion = sixaxis_fusion_parameters;

    return ResultSuccess;
}

Result SixAxis::GetSixAxisFusionParameters(
    const Core::HID::SixAxisSensorHandle& sixaxis_handle,
    Core::HID::SixAxisSensorFusionParameters& parameters) const {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    const auto& sixaxis = GetSixaxisState(sixaxis_handle);
    parameters = sixaxis.fusion;

    return ResultSuccess;
}

SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
    const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
    auto& controller = GetControllerFromHandle(sixaxis_handle);
    switch (sixaxis_handle.npad_type) {
    case Core::HID::NpadStyleIndex::Fullkey:
    case Core::HID::NpadStyleIndex::Pokeball:
        return controller.sixaxis_fullkey;
    case Core::HID::NpadStyleIndex::Handheld:
        return controller.sixaxis_handheld;
    case Core::HID::NpadStyleIndex::JoyconDual:
        if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
            return controller.sixaxis_dual_left;
        }
        return controller.sixaxis_dual_right;
    case Core::HID::NpadStyleIndex::JoyconLeft:
        return controller.sixaxis_left;
    case Core::HID::NpadStyleIndex::JoyconRight:
        return controller.sixaxis_right;
    default:
        return controller.sixaxis_unknown;
    }
}

const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
    const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
    const auto& controller = GetControllerFromHandle(sixaxis_handle);
    switch (sixaxis_handle.npad_type) {
    case Core::HID::NpadStyleIndex::Fullkey:
    case Core::HID::NpadStyleIndex::Pokeball:
        return controller.sixaxis_fullkey;
    case Core::HID::NpadStyleIndex::Handheld:
        return controller.sixaxis_handheld;
    case Core::HID::NpadStyleIndex::JoyconDual:
        if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
            return controller.sixaxis_dual_left;
        }
        return controller.sixaxis_dual_right;
    case Core::HID::NpadStyleIndex::JoyconLeft:
        return controller.sixaxis_left;
    case Core::HID::NpadStyleIndex::JoyconRight:
        return controller.sixaxis_right;
    default:
        return controller.sixaxis_unknown;
    }
}

SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
    const Core::HID::SixAxisSensorHandle& device_handle) {
    const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
    return GetControllerFromNpadIdType(npad_id);
}

const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
    const Core::HID::SixAxisSensorHandle& device_handle) const {
    const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
    return GetControllerFromNpadIdType(npad_id);
}

SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
    if (!IsNpadIdValid(npad_id)) {
        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
        npad_id = Core::HID::NpadIdType::Player1;
    }
    const auto npad_index = NpadIdTypeToIndex(npad_id);
    return controller_data[npad_index];
}

const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(
    Core::HID::NpadIdType npad_id) const {
    if (!IsNpadIdValid(npad_id)) {
        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
        npad_id = Core::HID::NpadIdType::Player1;
    }
    const auto npad_index = NpadIdTypeToIndex(npad_id);
    return controller_data[npad_index];
}

} // 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 "hid_core/hid_types.h"
#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/ring_lifo.h"

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

namespace Service::HID {
class NPad;

class SixAxis final : public ControllerBase {
public:
    explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_);
    ~SixAxis() 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 SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                     Core::HID::GyroscopeZeroDriftMode drift_mode);
    Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                     Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
    Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                 bool& is_at_rest) const;
    Result EnableSixAxisSensorUnalteredPassthrough(
        const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
    Result IsSixAxisSensorUnalteredPassthroughEnabled(
        const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
    Result LoadSixAxisSensorCalibrationParameter(
        const Core::HID::SixAxisSensorHandle& sixaxis_handle,
        Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
    Result GetSixAxisSensorIcInformation(
        const Core::HID::SixAxisSensorHandle& sixaxis_handle,
        Core::HID::SixAxisSensorIcInformation& ic_information) const;
    Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                             bool sixaxis_status);
    Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                        bool& is_fusion_enabled) const;
    Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                   bool is_fusion_enabled);
    Result SetSixAxisFusionParameters(
        const Core::HID::SixAxisSensorHandle& sixaxis_handle,
        Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
    Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
                                      Core::HID::SixAxisSensorFusionParameters& parameters) const;

private:
    static constexpr std::size_t NPAD_COUNT = 10;

    struct SixaxisParameters {
        bool is_fusion_enabled{true};
        bool unaltered_passthrough{false};
        Core::HID::SixAxisSensorFusionParameters fusion{};
        Core::HID::SixAxisSensorCalibrationParameter calibration{};
        Core::HID::SixAxisSensorIcInformation ic_information{};
        Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
            Core::HID::GyroscopeZeroDriftMode::Standard};
    };

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

        // Motion parameters
        bool sixaxis_at_rest{true};
        bool sixaxis_sensor_enabled{true};
        SixaxisParameters sixaxis_fullkey{};
        SixaxisParameters sixaxis_handheld{};
        SixaxisParameters sixaxis_dual_left{};
        SixaxisParameters sixaxis_dual_right{};
        SixaxisParameters sixaxis_left{};
        SixaxisParameters sixaxis_right{};
        SixaxisParameters sixaxis_unknown{};

        // Current pad state
        Core::HID::SixAxisSensorState sixaxis_fullkey_state{};
        Core::HID::SixAxisSensorState sixaxis_handheld_state{};
        Core::HID::SixAxisSensorState sixaxis_dual_left_state{};
        Core::HID::SixAxisSensorState sixaxis_dual_right_state{};
        Core::HID::SixAxisSensorState sixaxis_left_lifo_state{};
        Core::HID::SixAxisSensorState sixaxis_right_lifo_state{};
        int callback_key{};
    };

    SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
    const SixaxisParameters& GetSixaxisState(
        const Core::HID::SixAxisSensorHandle& device_handle) const;

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

    std::shared_ptr<NPad> npad;
    std::array<NpadControllerData, NPAD_COUNT> controller_data{};
};
} // namespace Service::HID

repo_name: litucks/torzu

license: mit


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

#include "core/core_timing.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/system_buttons/capture_button.h"

namespace Service::HID {

CaptureButton::CaptureButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}

CaptureButton::~CaptureButton() = default;

void CaptureButton::OnInit() {}

void CaptureButton::OnRelease() {}

void CaptureButton::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;
    }

    auto& shared_memory = data->shared_memory_format->capture_button;

    if (!IsControllerActivated()) {
        shared_memory.capture_lifo.buffer_count = 0;
        shared_memory.capture_lifo.buffer_tail = 0;
        return;
    }

    const auto& last_entry = shared_memory.capture_lifo.ReadCurrentEntry().state;
    next_state.sampling_number = last_entry.sampling_number + 1;

    auto* controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
    next_state.buttons.raw = controller->GetHomeButtons().raw;

    shared_memory.capture_lifo.WriteNextEntry(next_state);
}

} // namespace Service::HID


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

#pragma once

#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/system_buttons/system_button_types.h"

namespace Service::HID {

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


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

#include "core/core_timing.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/system_buttons/home_button.h"

namespace Service::HID {

HomeButton::HomeButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}

HomeButton::~HomeButton() = default;

void HomeButton::OnInit() {}

void HomeButton::OnRelease() {}

void HomeButton::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;
    }

    auto& shared_memory = data->shared_memory_format->home_button;

    if (!IsControllerActivated()) {
        shared_memory.home_lifo.buffer_count = 0;
        shared_memory.home_lifo.buffer_tail = 0;
        return;
    }

    const auto& last_entry = shared_memory.home_lifo.ReadCurrentEntry().state;
    next_state.sampling_number = last_entry.sampling_number + 1;

    auto* controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
    next_state.buttons.raw = controller->GetHomeButtons().raw;

    shared_memory.home_lifo.WriteNextEntry(next_state);
}

} // namespace Service::HID


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

#pragma once

#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/system_buttons/system_button_types.h"

namespace Service::HID {

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


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

#include "core/core_timing.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/system_buttons/sleep_button.h"

namespace Service::HID {

SleepButton::SleepButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}

SleepButton::~SleepButton() = default;

void SleepButton::OnInit() {}

void SleepButton::OnRelease() {}

void SleepButton::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;
    }

    auto& shared_memory = data->shared_memory_format->sleep_button;

    if (!IsControllerActivated()) {
        shared_memory.sleep_lifo.buffer_count = 0;
        shared_memory.sleep_lifo.buffer_tail = 0;
        return;
    }

    const auto& last_entry = shared_memory.sleep_lifo.ReadCurrentEntry().state;
    next_state.sampling_number = last_entry.sampling_number + 1;

    next_state.buttons.raw = 0;

    shared_memory.sleep_lifo.WriteNextEntry(next_state);
}

} // namespace Service::HID


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

#pragma once

#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/system_buttons/system_button_types.h"

namespace Service::HID {

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


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

#pragma once

#include "hid_core/hid_types.h"

namespace Service::HID {

// This is nn::hid::system::SleepButtonState
struct SleepButtonState {
    s64 sampling_number{};
    Core::HID::SleepButtonState buttons;
};
static_assert(sizeof(SleepButtonState) == 0x10, "SleepButtonState is an invalid size");

// This is nn::hid::system::HomeButtonState
struct HomeButtonState {
    s64 sampling_number{};
    Core::HID::HomeButtonState buttons;
};
static_assert(sizeof(HomeButtonState) == 0x10, "HomeButtonState is an invalid size");

// This is nn::hid::system::SleepButtonState
struct CaptureButtonState {
    s64 sampling_number{};
    Core::HID::CaptureButtonState buttons;
};
static_assert(sizeof(CaptureButtonState) == 0x10, "CaptureButtonState is an invalid size");

} // namespace Service::HID


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

#include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen_resource.h"

namespace Service::HID {

Gesture::Gesture(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}

Gesture::~Gesture() = default;

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

    // TODO: Result result = CreateThread();
    Result result = ResultSuccess;
    if (result.IsError()) {
        return result;
    }

    result = touch_resource->ActivateGesture();

    if (result.IsError()) {
        // TODO: StopThread();
    }

    return result;
}

Result Gesture::Activate(u64 aruid, u32 basic_gesture_id) {
    std::scoped_lock lock{mutex};
    return touch_resource->ActivateGesture(aruid, basic_gesture_id);
}

Result Gesture::Deactivate() {
    std::scoped_lock lock{mutex};
    const auto result = touch_resource->DeactivateGesture();

    if (result.IsError()) {
        return result;
    }

    // TODO: return StopThread();
    return ResultSuccess;
}

Result Gesture::IsActive(bool& out_is_active) const {
    out_is_active = touch_resource->IsGestureActive();
    return ResultSuccess;
}

} // 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::HID {
class TouchResource;

/// Handles gesture request from HID interfaces
class Gesture {
public:
    Gesture(std::shared_ptr<TouchResource> resource);
    ~Gesture();

    Result Activate();
    Result Activate(u64 aruid, u32 basic_gesture_id);

    Result Deactivate();

    Result IsActive(bool& out_is_active) const;

private:
    mutable std::mutex mutex;
    std::shared_ptr<TouchResource> touch_resource;
};

} // namespace Service::HID


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

#include "common/math_util.h"
#include "hid_core/resources/touch_screen/gesture_handler.h"

namespace Service::HID {

constexpr f32 Square(s32 num) {
    return static_cast<f32>(num * num);
}

GestureHandler::GestureHandler() {}

GestureHandler::~GestureHandler() {}

void GestureHandler::SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp) {
    gesture = {};
    gesture.active_points = std::min(MaxPoints, static_cast<std::size_t>(count));

    for (size_t id = 0; id < gesture.active_points; ++id) {
        const auto& [active_x, active_y] = touch_state[id].position;
        gesture.points[id] = {
            .x = static_cast<s32>(active_x),
            .y = static_cast<s32>(active_y),
        };

        gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
        gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
    }

    for (size_t id = 0; id < gesture.active_points; ++id) {
        const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
                                       Square(gesture.mid_point.y - gesture.points[id].y));
        gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
    }

    gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
                               static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));

    gesture.detection_count = last_gesture.detection_count;

    if (last_update_timestamp > timestamp) {
        timestamp = last_tap_timestamp;
    }

    time_difference = static_cast<f32>(timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
}

bool GestureHandler::NeedsUpdate() {
    if (force_update) {
        force_update = false;
        return true;
    }

    // Update if coordinates change
    for (size_t id = 0; id < MaxPoints; id++) {
        if (gesture.points[id] != last_gesture.points[id]) {
            return true;
        }
    }

    // Update on press and hold event after 0.5 seconds
    if (last_gesture_state.type == GestureType::Touch && last_gesture_state.point_count == 1 &&
        time_difference > PressDelay) {
        return enable_press_and_tap;
    }

    return false;
}

void GestureHandler::UpdateGestureState(GestureState& next_state, s64 timestamp) {
    last_update_timestamp = timestamp;

    GestureType type = GestureType::Idle;
    GestureAttribute attributes{};

    // Reset next state to default
    next_state.sampling_number = last_gesture_state.sampling_number + 1;
    next_state.delta = {};
    next_state.vel_x = 0;
    next_state.vel_y = 0;
    next_state.direction = GestureDirection::None;
    next_state.rotation_angle = 0;
    next_state.scale = 0;

    if (gesture.active_points > 0) {
        if (last_gesture.active_points == 0) {
            NewGesture(type, attributes);
        } else {
            UpdateExistingGesture(next_state, type);
        }
    } else {
        EndGesture(next_state, type, attributes);
    }

    // Apply attributes
    next_state.detection_count = gesture.detection_count;
    next_state.type = type;
    next_state.attributes = attributes;
    next_state.pos = gesture.mid_point;
    next_state.point_count = static_cast<s32>(gesture.active_points);
    next_state.points = gesture.points;
    last_gesture = gesture;
    last_gesture_state = next_state;
}

void GestureHandler::NewGesture(GestureType& type, GestureAttribute& attributes) {
    gesture.detection_count++;
    type = GestureType::Touch;

    // New touch after cancel is not considered new
    if (last_gesture_state.type != GestureType::Cancel) {
        attributes.is_new_touch.Assign(1);
        enable_press_and_tap = true;
    }
}

void GestureHandler::UpdateExistingGesture(GestureState& next_state, GestureType& type) {
    // Promote to pan type if touch moved
    for (size_t id = 0; id < MaxPoints; id++) {
        if (gesture.points[id] != last_gesture.points[id]) {
            type = GestureType::Pan;
            break;
        }
    }

    // Number of fingers changed cancel the last event and clear data
    if (gesture.active_points != last_gesture.active_points) {
        type = GestureType::Cancel;
        enable_press_and_tap = false;
        gesture.active_points = 0;
        gesture.mid_point = {};
        gesture.points.fill({});
        return;
    }

    // Calculate extra parameters of panning
    if (type == GestureType::Pan) {
        UpdatePanEvent(next_state, type);
        return;
    }

    // Promote to press type
    if (last_gesture_state.type == GestureType::Touch) {
        type = GestureType::Press;
    }
}

void GestureHandler::EndGesture(GestureState& next_state, GestureType& type,
                                GestureAttribute& attributes) {
    if (last_gesture.active_points != 0) {
        switch (last_gesture_state.type) {
        case GestureType::Touch:
            if (enable_press_and_tap) {
                SetTapEvent(type, attributes);
                return;
            }
            type = GestureType::Cancel;
            force_update = true;
            break;
        case GestureType::Press:
        case GestureType::Tap:
        case GestureType::Swipe:
        case GestureType::Pinch:
        case GestureType::Rotate:
            type = GestureType::Complete;
            force_update = true;
            break;
        case GestureType::Pan:
            EndPanEvent(next_state, type);
            break;
        default:
            break;
        }
        return;
    }
    if (last_gesture_state.type == GestureType::Complete ||
        last_gesture_state.type == GestureType::Cancel) {
        gesture.detection_count++;
    }
}

void GestureHandler::SetTapEvent(GestureType& type, GestureAttribute& attributes) {
    type = GestureType::Tap;
    gesture = last_gesture;
    force_update = true;
    f32 tap_time_difference =
        static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
    last_tap_timestamp = last_update_timestamp;
    if (tap_time_difference < DoubleTapDelay) {
        attributes.is_double_tap.Assign(1);
    }
}

void GestureHandler::UpdatePanEvent(GestureState& next_state, GestureType& type) {
    next_state.delta = gesture.mid_point - last_gesture_state.pos;
    next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
    next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
    last_pan_time_difference = time_difference;

    // Promote to pinch type
    if (std::abs(gesture.average_distance - last_gesture.average_distance) > PinchThreshold) {
        type = GestureType::Pinch;
        next_state.scale = gesture.average_distance / last_gesture.average_distance;
    }

    const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture.angle) /
                                                  (1 + (gesture.angle * last_gesture.angle)));
    // Promote to rotate type
    if (std::abs(angle_between_two_lines) > AngleThreshold) {
        type = GestureType::Rotate;
        next_state.scale = 0;
        next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
    }
}

void GestureHandler::EndPanEvent(GestureState& next_state, GestureType& type) {
    next_state.vel_x =
        static_cast<f32>(last_gesture_state.delta.x) / (last_pan_time_difference + time_difference);
    next_state.vel_y =
        static_cast<f32>(last_gesture_state.delta.y) / (last_pan_time_difference + time_difference);
    const f32 curr_vel =
        std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));

    // Set swipe event with parameters
    if (curr_vel > SwipeThreshold) {
        SetSwipeEvent(next_state, type);
        return;
    }

    // End panning without swipe
    type = GestureType::Complete;
    next_state.vel_x = 0;
    next_state.vel_y = 0;
    force_update = true;
}

void GestureHandler::SetSwipeEvent(GestureState& next_state, GestureType& type) {
    type = GestureType::Swipe;
    gesture = last_gesture;
    force_update = true;
    next_state.delta = last_gesture_state.delta;

    if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
        if (next_state.delta.x > 0) {
            next_state.direction = GestureDirection::Right;
            return;
        }
        next_state.direction = GestureDirection::Left;
        return;
    }
    if (next_state.delta.y > 0) {
        next_state.direction = GestureDirection::Down;
        return;
    }
    next_state.direction = GestureDirection::Up;
}

} // namespace Service::HID


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

#pragma once

#include <span>

#include "hid_core/resources/touch_screen/touch_types.h"

namespace Service::HID {

class GestureHandler {
public:
    GestureHandler();
    ~GestureHandler();

    void SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp);

    bool NeedsUpdate();
    void UpdateGestureState(GestureState& next_state, s64 timestamp);

private:
    // Initializes new gesture
    void NewGesture(GestureType& type, GestureAttribute& attributes);

    // Updates existing gesture state
    void UpdateExistingGesture(GestureState& next_state, GestureType& type);

    // Terminates exiting gesture
    void EndGesture(GestureState& next_state, GestureType& type, GestureAttribute& attributes);

    // Set current event to a tap event
    void SetTapEvent(GestureType& type, GestureAttribute& attributes);

    // Calculates and set the extra parameters related to a pan event
    void UpdatePanEvent(GestureState& next_state, GestureType& type);

    // Terminates the pan event
    void EndPanEvent(GestureState& next_state, GestureType& type);

    // Set current event to a swipe event
    void SetSwipeEvent(GestureState& next_state, GestureType& type);

    GestureProperties gesture{};
    GestureProperties last_gesture{};
    GestureState last_gesture_state{};
    s64 last_update_timestamp{};
    s64 last_tap_timestamp{};
    f32 last_pan_time_difference{};
    f32 time_difference{};
    bool force_update{true};
    bool enable_press_and_tap{false};
};

} // namespace Service::HID


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

#include "hid_core/hid_types.h"
#include "hid_core/resources/touch_screen/touch_screen.h"
#include "hid_core/resources/touch_screen/touch_screen_resource.h"

namespace Service::HID {

TouchScreen::TouchScreen(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}

TouchScreen::~TouchScreen() = default;

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

    // TODO: Result result = CreateThread();
    Result result = ResultSuccess;
    if (result.IsError()) {
        return result;
    }

    result = touch_resource->ActivateTouch();
    if (result.IsError()) {
        // TODO: StopThread();
    }

    return result;
}

Result TouchScreen::Activate(u64 aruid) {
    std::scoped_lock lock{mutex};
    return touch_resource->ActivateTouch(aruid);
}

Result TouchScreen::Deactivate() {
    std::scoped_lock lock{mutex};
    const auto result = touch_resource->DeactivateTouch();

    if (result.IsError()) {
        return result;
    }

    // TODO: return StopThread();
    return ResultSuccess;
}

Result TouchScreen::IsActive(bool& out_is_active) const {
    out_is_active = touch_resource->IsTouchActive();
    return ResultSuccess;
}

Result TouchScreen::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
    std::scoped_lock lock{mutex};
    return touch_resource->SetTouchScreenAutoPilotState(auto_pilot_state);
}

Result TouchScreen::UnsetTouchScreenAutoPilotState() {
    std::scoped_lock lock{mutex};
    return touch_resource->UnsetTouchScreenAutoPilotState();
}

Result TouchScreen::RequestNextTouchInput() {
    std::scoped_lock lock{mutex};
    return touch_resource->RequestNextTouchInput();
}

Result TouchScreen::RequestNextDummyInput() {
    std::scoped_lock lock{mutex};
    return touch_resource->RequestNextDummyInput();
}

Result TouchScreen::ProcessTouchScreenAutoTune() {
    std::scoped_lock lock{mutex};
    return touch_resource->ProcessTouchScreenAutoTune();
}

Result TouchScreen::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x,
                                                f32 point2_y) {
    std::scoped_lock lock{mutex};
    touch_resource->SetTouchScreenMagnification(point1_x, point1_y, point2_x, point2_y);
    return ResultSuccess;
}

Result TouchScreen::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
    std::scoped_lock lock{mutex};
    return touch_resource->SetTouchScreenResolution(width, height, aruid);
}

Result TouchScreen::SetTouchScreenConfiguration(
    const Core::HID::TouchScreenConfigurationForNx& mode, u64 aruid) {
    std::scoped_lock lock{mutex};
    return touch_resource->SetTouchScreenConfiguration(mode, aruid);
}

Result TouchScreen::GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode,
                                                u64 aruid) const {
    std::scoped_lock lock{mutex};
    return touch_resource->GetTouchScreenConfiguration(out_mode, aruid);
}

Result TouchScreen::SetTouchScreenDefaultConfiguration(
    const Core::HID::TouchScreenConfigurationForNx& mode) {
    std::scoped_lock lock{mutex};
    return touch_resource->SetTouchScreenDefaultConfiguration(mode);
}

Result TouchScreen::GetTouchScreenDefaultConfiguration(
    Core::HID::TouchScreenConfigurationForNx& out_mode) const {
    std::scoped_lock lock{mutex};
    return touch_resource->GetTouchScreenDefaultConfiguration(out_mode);
}

void TouchScreen::OnTouchUpdate(u64 timestamp) {
    std::scoped_lock lock{mutex};
    touch_resource->OnTouchUpdate(timestamp);
}

} // 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 Core::HID {
struct TouchScreenConfigurationForNx;
}

namespace Core::Timing {
struct EventType;
}

namespace Service::HID {
class TouchResource;
struct AutoPilotState;

/// Handles touch request from HID interfaces
class TouchScreen {
public:
    TouchScreen(std::shared_ptr<TouchResource> resource);
    ~TouchScreen();

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

    Result Deactivate();

    Result IsActive(bool& out_is_active) const;

    Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state);
    Result UnsetTouchScreenAutoPilotState();

    Result RequestNextTouchInput();
    Result RequestNextDummyInput();

    Result ProcessTouchScreenAutoTune();

    Result SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y);
    Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid);

    Result SetTouchScreenConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode,
                                       u64 aruid);
    Result GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode,
                                       u64 aruid) const;

    Result SetTouchScreenDefaultConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode);
    Result GetTouchScreenDefaultConfiguration(
        Core::HID::TouchScreenConfigurationForNx& out_mode) const;

    void OnTouchUpdate(u64 timestamp);

private:
    mutable std::mutex mutex;
    std::shared_ptr<TouchResource> touch_resource;
    std::shared_ptr<Core::Timing::EventType> touch_update_event;
};

} // namespace Service::HID


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

#include <algorithm>
#include "common/settings.h"
#include "core/frontend/emu_window.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/touch_screen/touch_screen_driver.h"

namespace Service::HID {

TouchDriver::TouchDriver(Core::HID::HIDCore& hid_core) {
    console = hid_core.GetEmulatedConsole();
}

TouchDriver::~TouchDriver() = default;

Result TouchDriver::StartTouchSensor() {
    is_running = true;
    return ResultSuccess;
}

Result TouchDriver::StopTouchSensor() {
    is_running = false;
    return ResultSuccess;
}

bool TouchDriver::IsRunning() const {
    return is_running;
}

void TouchDriver::ProcessTouchScreenAutoTune() const {
    // TODO
}

Result TouchDriver::WaitForDummyInput() {
    touch_status = {};
    return ResultSuccess;
}

Result TouchDriver::WaitForInput() {
    touch_status = {};
    const auto touch_input = console->GetTouch();
    for (std::size_t id = 0; id < touch_status.states.size(); id++) {
        const auto& current_touch = touch_input[id];
        auto& finger = fingers[id];
        finger.id = current_touch.id;

        if (finger.attribute.start_touch) {
            finger.attribute.raw = 0;
            continue;
        }

        if (finger.attribute.end_touch) {
            finger.attribute.raw = 0;
            finger.pressed = false;
            continue;
        }

        if (!finger.pressed && current_touch.pressed) {
            finger.attribute.start_touch.Assign(1);
            finger.pressed = true;
            finger.position = current_touch.position;
            continue;
        }

        if (finger.pressed && !current_touch.pressed) {
            finger.attribute.raw = 0;
            finger.attribute.end_touch.Assign(1);
            continue;
        }

        // Only update position if touch is not on a special frame
        finger.position = current_touch.position;
    }

    std::array<Core::HID::TouchFinger, MaxFingers> active_fingers;
    const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
                                       [](const auto& finger) { return finger.pressed; });
    const auto active_fingers_count =
        static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));

    touch_status.entry_count = static_cast<s32>(active_fingers_count);
    for (std::size_t id = 0; id < MaxFingers; ++id) {
        auto& touch_entry = touch_status.states[id];
        if (id < active_fingers_count) {
            const auto& [active_x, active_y] = active_fingers[id].position;
            touch_entry.position = {
                .x = static_cast<u16>(active_x * TouchSensorWidth),
                .y = static_cast<u16>(active_y * TouchSensorHeight),
            };
            touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
            touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
            touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
            touch_entry.finger = active_fingers[id].id;
            touch_entry.attribute.raw = active_fingers[id].attribute.raw;
        }
    }
    return ResultSuccess;
}

void TouchDriver::GetNextTouchState(TouchScreenState& out_state) const {
    out_state = touch_status;
}

void TouchDriver::SetTouchMode(Core::HID::TouchScreenModeForNx mode) {
    touch_mode = mode;
}

Core::HID::TouchScreenModeForNx TouchDriver::GetTouchMode() const {
    return touch_mode;
}

} // namespace Service::HID


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

#pragma once

#include "common/common_types.h"
#include "core/hle/result.h"
#include "hid_core/frontend/emulated_console.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/touch_screen/touch_types.h"

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

namespace Service::HID {

/// This handles all request to Ftm3bd56(TouchPanel) hardware
class TouchDriver {
public:
    explicit TouchDriver(Core::HID::HIDCore& hid_core);
    ~TouchDriver();

    Result StartTouchSensor();
    Result StopTouchSensor();
    bool IsRunning() const;

    void ProcessTouchScreenAutoTune() const;

    Result WaitForDummyInput();
    Result WaitForInput();

    void GetNextTouchState(TouchScreenState& out_state) const;

    void SetTouchMode(Core::HID::TouchScreenModeForNx mode);
    Core::HID::TouchScreenModeForNx GetTouchMode() const;

private:
    bool is_running{};
    TouchScreenState touch_status{};
    Core::HID::TouchFingerState fingers{};
    Core::HID::TouchScreenModeForNx touch_mode{};

    Core::HID::EmulatedConsole* console = nullptr;
};

} // namespace Service::HID


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

#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.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/touch_screen/touch_screen_driver.h"
#include "hid_core/resources/touch_screen/touch_screen_resource.h"

namespace Service::HID {
constexpr auto GestureUpdatePeriod = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)

TouchResource::TouchResource(Core::System& system_) : system{system_} {
    m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys");
}

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

Result TouchResource::ActivateTouch() {
    if (global_ref_counter == std::numeric_limits<s32>::max() - 1 ||
        touch_ref_counter == std::numeric_limits<s32>::max() - 1) {
        return ResultTouchOverflow;
    }

    if (global_ref_counter == 0) {
        std::scoped_lock lock{*shared_mutex};

        const auto result = touch_driver->StartTouchSensor();
        if (result.IsError()) {
            return result;
        }

        is_initalized = true;
        system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod,
                                                 timer_event);
        current_touch_state = {};
        ReadTouchInput();
        gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
                                      0);
    }

    Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard};
    m_set_sys->GetTouchScreenMode(&touch_mode);
    default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode);

    global_ref_counter++;
    touch_ref_counter++;
    return ResultSuccess;
}

Result TouchResource::ActivateTouch(u64 aruid) {
    std::scoped_lock lock{*shared_mutex};

    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
        TouchAruidData& touch_data = aruid_data[aruid_index];

        if (applet_data == nullptr || !applet_data->flag.is_assigned) {
            touch_data = {};
            continue;
        }

        const u64 aruid_id = applet_data->aruid;
        if (touch_data.aruid != aruid_id) {
            touch_data = {};
            touch_data.aruid = aruid_id;
        }

        if (aruid != aruid_id) {
            continue;
        }

        auto& touch_shared = applet_data->shared_memory_format->touch_screen;

        if (touch_shared.touch_screen_lifo.buffer_count == 0) {
            StorePreviousTouchState(previous_touch_state, touch_data.finger_map,
                                    current_touch_state,
                                    applet_data->flag.enable_touchscreen.Value() != 0);
            touch_shared.touch_screen_lifo.WriteNextEntry(previous_touch_state);
        }
    }
    return ResultSuccess;
}

Result TouchResource::ActivateGesture() {
    if (global_ref_counter == std::numeric_limits<s32>::max() - 1 ||
        gesture_ref_counter == std::numeric_limits<s32>::max() - 1) {
        return ResultGestureOverflow;
    }

    // Initialize first instance
    if (global_ref_counter == 0) {
        const auto result = touch_driver->StartTouchSensor();
        if (result.IsError()) {
            return result;
        }

        is_initalized = true;
        system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod,
                                                 timer_event);
        current_touch_state = {};
        ReadTouchInput();
        gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
                                      0);
    }

    global_ref_counter++;
    gesture_ref_counter++;
    return ResultSuccess;
}

Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) {
    std::scoped_lock lock{*shared_mutex};

    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
        TouchAruidData& touch_data = aruid_data[aruid_index];

        if (applet_data == nullptr || !applet_data->flag.is_assigned) {
            touch_data = {};
            continue;
        }

        const u64 aruid_id = applet_data->aruid;
        if (touch_data.aruid != aruid_id) {
            touch_data = {};
            touch_data.aruid = aruid_id;
        }

        if (aruid != aruid_id) {
            continue;
        }

        auto& gesture_shared = applet_data->shared_memory_format->gesture;
        if (touch_data.basic_gesture_id != basic_gesture_id) {
            gesture_shared.gesture_lifo.buffer_count = 0;
        }

        if (gesture_shared.gesture_lifo.buffer_count == 0) {
            touch_data.basic_gesture_id = basic_gesture_id;

            gesture_shared.gesture_lifo.WriteNextEntry(gesture_state);
        }
    }

    return ResultSuccess;
}

Result TouchResource::DeactivateTouch() {
    if (touch_ref_counter == 0 || global_ref_counter == 0) {
        return ResultTouchNotInitialized;
    }

    global_ref_counter--;
    touch_ref_counter--;

    if (touch_ref_counter + global_ref_counter != 0) {
        return ResultSuccess;
    }

    return Finalize();
}

Result TouchResource::DeactivateGesture() {
    if (gesture_ref_counter == 0 || global_ref_counter == 0) {
        return ResultGestureNotInitialized;
    }

    global_ref_counter--;
    gesture_ref_counter--;

    if (touch_ref_counter + global_ref_counter != 0) {
        return ResultSuccess;
    }

    return Finalize();
}

bool TouchResource::IsTouchActive() const {
    return touch_ref_counter != 0;
}

bool TouchResource::IsGestureActive() const {
    return gesture_ref_counter != 0;
}

void TouchResource::SetTouchDriver(std::shared_ptr<TouchDriver> driver) {
    touch_driver = driver;
}

void TouchResource::SetAppletResource(std::shared_ptr<AppletResource> shared,
                                      std::recursive_mutex* mutex) {
    applet_resource = shared;
    shared_mutex = mutex;
}

void TouchResource::SetInputEvent(Kernel::KEvent* event, std::mutex* mutex) {
    input_event = event;
    input_mutex = mutex;
}

void TouchResource::SetHandheldConfig(std::shared_ptr<HandheldConfig> config) {
    handheld_config = config;
}

void TouchResource::SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event) {
    timer_event = event;
}

Result TouchResource::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
    if (global_ref_counter == 0) {
        return ResultTouchNotInitialized;
    }

    if (!is_auto_pilot_initialized) {
        is_auto_pilot_initialized = true;
        auto_pilot = {};
    }

    TouchScreenState state = {
        .entry_count = static_cast<s32>(auto_pilot_state.count),
        .states = auto_pilot_state.state,
    };

    SanitizeInput(state);

    auto_pilot.count = state.entry_count;
    auto_pilot.state = state.states;
    return ResultSuccess;
}

Result TouchResource::UnsetTouchScreenAutoPilotState() {
    if (global_ref_counter == 0) {
        return ResultTouchNotInitialized;
    }

    is_auto_pilot_initialized = false;
    auto_pilot = {};
    return ResultSuccess;
}

Result TouchResource::RequestNextTouchInput() {
    if (global_ref_counter == 0) {
        return ResultTouchNotInitialized;
    }

    if (handheld_config->is_handheld_hid_enabled) {
        const Result result = touch_driver->WaitForInput();
        if (result.IsError()) {
            return result;
        }
    }

    is_initalized = true;
    return ResultSuccess;
}

Result TouchResource::RequestNextDummyInput() {
    if (global_ref_counter == 0) {
        return ResultTouchNotInitialized;
    }

    if (handheld_config->is_handheld_hid_enabled) {
        const Result result = touch_driver->WaitForDummyInput();
        if (result.IsError()) {
            return result;
        }
    }

    is_initalized = false;
    return ResultSuccess;
}

Result TouchResource::ProcessTouchScreenAutoTune() {
    touch_driver->ProcessTouchScreenAutoTune();
    return ResultSuccess;
}

void TouchResource::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x,
                                                f32 point2_y) {
    offset = {
        .x = point1_x,
        .y = point1_y,
    };
    magnification = {
        .x = point2_x,
        .y = point2_y,
    };
}

Result TouchResource::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
    std::scoped_lock lock{*shared_mutex};

    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
        TouchAruidData& data = aruid_data[aruid_index];

        if (!applet_data->flag.is_assigned) {
            continue;
        }
        if (aruid != data.aruid) {
            continue;
        }
        data.resolution_width = static_cast<u16>(width);
        data.resolution_height = static_cast<u16>(height);
    }

    return ResultSuccess;
}

Result TouchResource::SetTouchScreenConfiguration(
    const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid) {
    std::scoped_lock lock{*shared_mutex};

    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
        TouchAruidData& data = aruid_data[aruid_index];

        if (applet_data == nullptr || !applet_data->flag.is_assigned) {
            continue;
        }
        if (aruid != data.aruid) {
            continue;
        }
        data.finger_map.touch_mode = touch_configuration.mode;
    }

    return ResultSuccess;
}

Result TouchResource::GetTouchScreenConfiguration(
    Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const {
    std::scoped_lock lock{*shared_mutex};

    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
        const TouchAruidData& data = aruid_data[aruid_index];

        if (applet_data == nullptr || !applet_data->flag.is_assigned) {
            continue;
        }
        if (aruid != data.aruid) {
            continue;
        }
        out_touch_configuration.mode = data.finger_map.touch_mode;
    }

    return ResultSuccess;
}

Result TouchResource::SetTouchScreenDefaultConfiguration(
    const Core::HID::TouchScreenConfigurationForNx& touch_configuration) {
    default_touch_screen_mode = touch_configuration.mode;
    return ResultSuccess;
}

Result TouchResource::GetTouchScreenDefaultConfiguration(
    Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const {
    out_touch_configuration.mode = default_touch_screen_mode;
    return ResultSuccess;
}

Result TouchResource::Finalize() {
    is_auto_pilot_initialized = false;
    auto_pilot = {};
    system.CoreTiming().UnscheduleEvent(timer_event);

    const auto result = touch_driver->StopTouchSensor();
    if (result.IsError()) {
        return result;
    }

    is_initalized = false;
    return ResultSuccess;
}

void TouchResource::StorePreviousTouchState(TouchScreenState& out_previous_touch,
                                            TouchFingerMap& out_finger_map,
                                            const TouchScreenState& current_touch,
                                            bool is_touch_enabled) const {
    s32 finger_count{};

    if (is_touch_enabled) {
        finger_count = current_touch.entry_count;
        if (finger_count < 1) {
            out_finger_map.finger_count = 0;
            out_finger_map.finger_ids = {};
            out_previous_touch.sampling_number = current_touch.sampling_number;
            out_previous_touch.entry_count = 0;
            out_previous_touch.states = {};
            return;
        }
        for (std::size_t i = 0; i < static_cast<u32>(finger_count); i++) {
            out_finger_map.finger_ids[i] = current_touch.states[i].finger;
            out_previous_touch.states[i] = current_touch.states[i];
        }
        out_finger_map.finger_count = finger_count;
        return;
    }

    if (!is_touch_enabled && out_finger_map.finger_count > 0 && current_touch.entry_count > 0) {
        // TODO
    }

    // Zero out unused entries
    for (std::size_t i = finger_count; i < MaxFingers; i++) {
        out_finger_map.finger_ids[i] = 0;
        out_previous_touch.states[i] = {};
    }

    out_previous_touch.sampling_number = current_touch.sampling_number;
    out_previous_touch.entry_count = finger_count;
}

void TouchResource::ReadTouchInput() {
    previous_touch_state = current_touch_state;

    if (!is_initalized || !handheld_config->is_handheld_hid_enabled || !touch_driver->IsRunning()) {
        touch_driver->WaitForDummyInput();
    } else {
        touch_driver->WaitForInput();
    }

    touch_driver->GetNextTouchState(current_touch_state);
    SanitizeInput(current_touch_state);
    current_touch_state.sampling_number = sample_number;
    sample_number++;

    if (is_auto_pilot_initialized && current_touch_state.entry_count == 0) {
        const std::size_t finger_count = static_cast<std::size_t>(auto_pilot.count);
        current_touch_state.entry_count = static_cast<s32>(finger_count);
        for (std::size_t i = 0; i < finger_count; i++) {
            current_touch_state.states[i] = auto_pilot.state[i];
        }

        std::size_t index = 0;
        for (std::size_t i = 0; i < finger_count; i++) {
            if (auto_pilot.state[i].attribute.end_touch) {
                continue;
            }
            auto_pilot.state[i].attribute.raw = 0;
            auto_pilot.state[index] = auto_pilot.state[i];
            index++;
        }

        auto_pilot.count = index;
        for (std::size_t i = index; i < auto_pilot.state.size(); i++) {
            auto_pilot.state[i] = {};
        }
    }

    for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) {
        auto& state = current_touch_state.states[i];
        state.position.x = static_cast<u32>((magnification.y * static_cast<f32>(state.position.x)) +
                                            (offset.x * static_cast<f32>(TouchSensorWidth)));
        state.position.y = static_cast<u32>((magnification.y * static_cast<f32>(state.position.y)) +
                                            (offset.x * static_cast<f32>(TouchSensorHeight)));
        state.diameter_x = static_cast<u32>(magnification.x * static_cast<f32>(state.diameter_x));
        state.diameter_y = static_cast<u32>(magnification.y * static_cast<f32>(state.diameter_y));
    }

    std::size_t index = 0;
    for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) {
        const auto& old_state = current_touch_state.states[i];
        auto& state = current_touch_state.states[index];
        if ((TouchSensorWidth <= old_state.position.x) ||
            (TouchSensorHeight <= old_state.position.y)) {
            continue;
        }
        state = old_state;
        index++;
    }
    current_touch_state.entry_count = static_cast<s32>(index);

    SanitizeInput(current_touch_state);

    std::scoped_lock lock{*input_mutex};
    if (current_touch_state.entry_count == previous_touch_state.entry_count) {
        if (current_touch_state.entry_count < 1) {
            return;
        }
        bool has_moved = false;
        for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count);
             i++) {
            s32 delta_x = std::abs(static_cast<s32>(current_touch_state.states[i].position.x) -
                                   static_cast<s32>(previous_touch_state.states[i].position.x));
            s32 delta_y = std::abs(static_cast<s32>(current_touch_state.states[i].position.y) -
                                   static_cast<s32>(previous_touch_state.states[i].position.y));
            if (delta_x > 1 || delta_y > 1) {
                has_moved = true;
            }
        }
        if (!has_moved) {
            return;
        }
    }

    input_event->Signal();
}

void TouchResource::OnTouchUpdate(s64 timestamp) {
    if (global_ref_counter == 0) {
        return;
    }

    ReadTouchInput();
    gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
                                  timestamp);

    std::scoped_lock lock{*shared_mutex};

    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
        TouchAruidData& data = aruid_data[aruid_index];

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

        if (data.aruid != applet_data->aruid) {
            data = {};
            data.aruid = applet_data->aruid;
        }

        if (gesture_ref_counter != 0) {
            if (!applet_data->flag.enable_touchscreen) {
                gesture_state = {};
            }
            if (gesture_handler.NeedsUpdate()) {
                gesture_handler.UpdateGestureState(gesture_state, timestamp);
                auto& gesture_shared = applet_data->shared_memory_format->gesture;
                gesture_shared.gesture_lifo.WriteNextEntry(gesture_state);
            }
        }

        if (touch_ref_counter != 0) {
            auto touch_mode = data.finger_map.touch_mode;
            if (touch_mode == Core::HID::TouchScreenModeForNx::UseSystemSetting) {
                touch_mode = default_touch_screen_mode;
            }

            if (applet_resource->GetActiveAruid() == applet_data->aruid &&
                touch_mode != Core::HID::TouchScreenModeForNx::UseSystemSetting && is_initalized &&
                handheld_config->is_handheld_hid_enabled && touch_driver->IsRunning()) {
                touch_driver->SetTouchMode(touch_mode);
            }

            auto& touch_shared = applet_data->shared_memory_format->touch_screen;
            StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state,
                                    applet_data->flag.enable_touchscreen.As<bool>());
            touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
        }
    }
}

void TouchResource::SanitizeInput(TouchScreenState& state) const {
    for (std::size_t i = 0; i < static_cast<std::size_t>(state.entry_count); i++) {
        auto& entry = state.states[i];
        entry.position.x =
            std::clamp(entry.position.x, TouchBorders, TouchSensorWidth - TouchBorders - 1);
        entry.position.y =
            std::clamp(entry.position.y, TouchBorders, TouchSensorHeight - TouchBorders - 1);
        entry.diameter_x = std::clamp(entry.diameter_x, 0u, TouchSensorWidth - MaxTouchDiameter);
        entry.diameter_y = std::clamp(entry.diameter_y, 0u, TouchSensorHeight - MaxTouchDiameter);
        entry.rotation_angle =
            std::clamp(entry.rotation_angle, -MaxRotationAngle, MaxRotationAngle);
    }
}

} // namespace Service::HID


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

#pragma once

#include <array>
#include <mutex>

#include "common/common_types.h"
#include "common/point.h"
#include "core/hle/result.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/touch_screen/gesture_handler.h"
#include "hid_core/resources/touch_screen/touch_types.h"

namespace Core {
class System;
}

namespace Core::Timing {
struct EventType;
}

namespace Kernel {
class KEvent;
} // namespace Kernel

namespace Service::Set {
class ISystemSettingsServer;
}

namespace Service::HID {
class AppletResource;
class TouchSharedMemoryManager;
class TouchDriver;
struct HandheldConfig;

class TouchResource {
public:
    TouchResource(Core::System& system_);
    ~TouchResource();

    Result ActivateTouch();
    Result ActivateTouch(u64 aruid);

    Result ActivateGesture();
    Result ActivateGesture(u64 aruid, u32 basic_gesture_id);

    Result DeactivateTouch();
    Result DeactivateGesture();

    bool IsTouchActive() const;
    bool IsGestureActive() const;

    void SetTouchDriver(std::shared_ptr<TouchDriver> driver);
    void SetAppletResource(std::shared_ptr<AppletResource> shared, std::recursive_mutex* mutex);
    void SetInputEvent(Kernel::KEvent* event, std::mutex* mutex);
    void SetHandheldConfig(std::shared_ptr<HandheldConfig> config);
    void SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event);

    Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state);
    Result UnsetTouchScreenAutoPilotState();

    Result RequestNextTouchInput();
    Result RequestNextDummyInput();

    Result ProcessTouchScreenAutoTune();
    void SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y);
    Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid);

    Result SetTouchScreenConfiguration(
        const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid);
    Result GetTouchScreenConfiguration(
        Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const;

    Result SetTouchScreenDefaultConfiguration(
        const Core::HID::TouchScreenConfigurationForNx& touch_configuration);
    Result GetTouchScreenDefaultConfiguration(
        Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const;

    void OnTouchUpdate(s64 timestamp);

private:
    Result Finalize();

    void StorePreviousTouchState(TouchScreenState& out_previous_touch,
                                 TouchFingerMap& out_finger_map,
                                 const TouchScreenState& current_touch,
                                 bool is_touch_enabled) const;
    void ReadTouchInput();

    void SanitizeInput(TouchScreenState& state) const;

    s32 global_ref_counter{};
    s32 gesture_ref_counter{};
    s32 touch_ref_counter{};
    bool is_initalized{};
    u64 sample_number{};

    // External resources
    std::shared_ptr<Core::Timing::EventType> timer_event{nullptr};
    std::shared_ptr<TouchDriver> touch_driver{nullptr};
    std::shared_ptr<AppletResource> applet_resource{nullptr};
    std::recursive_mutex* shared_mutex{nullptr};
    std::shared_ptr<HandheldConfig> handheld_config{nullptr};
    Kernel::KEvent* input_event{nullptr};
    std::mutex* input_mutex{nullptr};

    // Internal state
    TouchScreenState current_touch_state{};
    TouchScreenState previous_touch_state{};
    GestureState gesture_state{};
    bool is_auto_pilot_initialized{};
    AutoPilotState auto_pilot{};
    GestureHandler gesture_handler{};
    std::array<TouchAruidData, 0x20> aruid_data{};
    Common::Point<f32> magnification{1.0f, 1.0f};
    Common::Point<f32> offset{0.0f, 0.0f};
    Core::HID::TouchScreenModeForNx default_touch_screen_mode{
        Core::HID::TouchScreenModeForNx::Finger};

    Core::System& system;
    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};

} // namespace Service::HID


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

#pragma once

#include <array>

#include <array>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/point.h"
#include "hid_core/hid_types.h"

namespace Service::HID {
constexpr std::size_t MaxFingers = 16;
constexpr std::size_t MaxPoints = 4;
constexpr u32 TouchSensorWidth = 1280;
constexpr u32 TouchSensorHeight = 720;
constexpr s32 MaxRotationAngle = 270;
constexpr u32 MaxTouchDiameter = 30;
constexpr u32 TouchBorders = 15;

// HW is around 700, value is set to 400 to make it easier to trigger with mouse
constexpr f32 SwipeThreshold = 400.0f; // Threshold in pixels/s
constexpr f32 AngleThreshold = 0.015f; // Threshold in radians
constexpr f32 PinchThreshold = 0.5f;   // Threshold in pixels
constexpr f32 PressDelay = 0.5f;       // Time in seconds
constexpr f32 DoubleTapDelay = 0.35f;  // Time in seconds

// This is nn::hid::GestureType
enum class GestureType : u32 {
    Idle,     // Nothing touching the screen
    Complete, // Set at the end of a touch event
    Cancel,   // Set when the number of fingers change
    Touch,    // A finger just touched the screen
    Press,    // Set if last type is touch and the finger hasn't moved
    Tap,      // Fast press then release
    Pan,      // All points moving together across the screen
    Swipe,    // Fast press movement and release of a single point
    Pinch,    // All points moving away/closer to the midpoint
    Rotate,   // All points rotating from the midpoint
    GestureTypeMax = Rotate,
};

// This is nn::hid::GestureDirection
enum class GestureDirection : u32 {
    None,
    Left,
    Up,
    Right,
    Down,
};

// This is nn::hid::GestureAttribute
struct GestureAttribute {
    union {
        u32 raw{};

        BitField<4, 1, u32> is_new_touch;
        BitField<8, 1, u32> is_double_tap;
    };
};
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");

// This is nn::hid::GestureState
struct GestureState {
    s64 sampling_number{};
    s64 detection_count{};
    GestureType type{GestureType::Idle};
    GestureDirection direction{GestureDirection::None};
    Common::Point<s32> pos{};
    Common::Point<s32> delta{};
    f32 vel_x{};
    f32 vel_y{};
    GestureAttribute attributes{};
    f32 scale{};
    f32 rotation_angle{};
    s32 point_count{};
    std::array<Common::Point<s32>, 4> points{};
};
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");

struct GestureProperties {
    std::array<Common::Point<s32>, MaxPoints> points{};
    std::size_t active_points{};
    Common::Point<s32> mid_point{};
    s64 detection_count{};
    u64 delta_time{};
    f32 average_distance{};
    f32 angle{};
};

// This is nn::hid::TouchState
struct TouchState {
    u64 delta_time{};
    Core::HID::TouchAttribute attribute{};
    u32 finger{};
    Common::Point<u32> position{};
    u32 diameter_x{};
    u32 diameter_y{};
    s32 rotation_angle{};
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");

// This is nn::hid::TouchScreenState
struct TouchScreenState {
    s64 sampling_number{};
    s32 entry_count{};
    INSERT_PADDING_BYTES(4); // Reserved
    std::array<TouchState, MaxFingers> states{};
};
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");

struct TouchFingerMap {
    s32 finger_count{};
    Core::HID::TouchScreenModeForNx touch_mode;
    INSERT_PADDING_BYTES(3);
    std::array<u32, MaxFingers> finger_ids{};
};
static_assert(sizeof(TouchFingerMap) == 0x48, "TouchFingerMap is an invalid size");

struct TouchAruidData {
    u64 aruid;
    u32 basic_gesture_id;
    u64 used_1;
    u64 used_2;
    u64 used_3;
    u64 used_4;
    GestureType gesture_type;
    u16 resolution_width;
    u16 resolution_height;
    TouchFingerMap finger_map;
};
static_assert(sizeof(TouchAruidData) == 0x80, "TouchAruidData is an invalid size");

struct AutoPilotState {
    u64 count;
    std::array<TouchState, 16> state;
};
static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size");

} // namespace Service::HID


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

#include "core/core_timing.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/unique_pad/unique_pad.h"

namespace Service::HID {

UniquePad::UniquePad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}

UniquePad::~UniquePad() = default;

void UniquePad::OnInit() {}

void UniquePad::OnRelease() {}

void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    const u64 aruid = applet_resource->GetActiveAruid();
    auto* data = applet_resource->GetAruidData(aruid);

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

    auto& header = data->shared_memory_format->unique_pad.header;
    header.timestamp = core_timing.GetGlobalTimeNs().count();
    header.total_entry_count = 17;
    header.entry_count = 0;
    header.last_entry_index = 0;
}

} // namespace Service::HID


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

#pragma once

#include "hid_core/resources/controller_base.h"

namespace Service::HID {

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


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

#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_result.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"

namespace Service::HID {

NpadGcVibrationDevice::NpadGcVibrationDevice() {}

Result NpadGcVibrationDevice::Activate() {
    if (ref_counter == 0 && is_mounted) {
        f32 volume = 1.0f;
        const auto result = vibration_handler->GetVibrationVolume(volume);
        if (result.IsSuccess()) {
            xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
        }
    }

    ref_counter++;
    return ResultSuccess;
}

Result NpadGcVibrationDevice::Deactivate() {
    if (ref_counter == 1 && is_mounted) {
        f32 volume = 1.0f;
        const auto result = vibration_handler->GetVibrationVolume(volume);
        if (result.IsSuccess()) {
            xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
        }
    }

    if (ref_counter > 0) {
        ref_counter--;
    }

    return ResultSuccess;
}

Result NpadGcVibrationDevice::Mount(IAbstractedPad& abstracted_pad, u32 slot,
                                    NpadVibration* handler) {
    if (!abstracted_pad.internal_flags.is_connected) {
        return ResultSuccess;
    }

    // TODO: This device doesn't use a xcd handle instead has an GC adapter handle. This is just to
    // keep compatibility with the front end.
    xcd_handle = abstracted_pad.xcd_handle;
    adapter_slot = slot;
    vibration_handler = handler;
    is_mounted = true;

    if (ref_counter == 0) {
        return ResultSuccess;
    }

    f32 volume{1.0f};
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsSuccess()) {
        xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
    }

    return ResultSuccess;
}

Result NpadGcVibrationDevice::Unmount() {
    if (ref_counter == 0 || !is_mounted) {
        is_mounted = false;
        return ResultSuccess;
    }

    f32 volume{1.0f};
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsSuccess()) {
        xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
    }

    is_mounted = false;
    return ResultSuccess;
}

Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command) {
    if (!is_mounted) {
        return ResultSuccess;
    }
    f32 volume = 1.0f;
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsError()) {
        return result;
    }
    if (volume == 0.0) {
        command = Core::HID::VibrationGcErmCommand::Stop;
    } else {
        if (command > Core::HID::VibrationGcErmCommand::StopHard) {
            // Abort
            return ResultSuccess;
        }
    }
    xcd_handle->SetVibration(adapter_slot, command);
    return ResultSuccess;
}

Result NpadGcVibrationDevice::GetActualVibrationGcErmCommand(
    Core::HID::VibrationGcErmCommand& out_command) {
    if (!is_mounted) {
        out_command = Core::HID::VibrationGcErmCommand::Stop;
        return ResultSuccess;
    }

    f32 volume = 1.0f;
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsError()) {
        return result;
    }
    if (volume == 0.0f) {
        out_command = Core::HID::VibrationGcErmCommand::Stop;
        return ResultSuccess;
    }

    // TODO: GetActualVibrationGcErmCommand
    return ResultSuccess;
}

Result NpadGcVibrationDevice::SendVibrationNotificationPattern(
    Core::HID::VibrationGcErmCommand command) {
    if (!is_mounted) {
        return ResultSuccess;
    }

    f32 volume = 1.0f;
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsError()) {
        return result;
    }
    if (volume <= 0.0f) {
        command = Core::HID::VibrationGcErmCommand::Stop;
    }
    if (command > Core::HID::VibrationGcErmCommand::StopHard) {
        // Abort
        return ResultSuccess;
    }

    // TODO: SendVibrationNotificationPattern
    return ResultSuccess;
}

} // namespace Service::HID


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

#pragma once

#include <array>
#include <mutex>

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

namespace Service::HID {
class NpadVibration;

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

    Result Activate() override;
    Result Deactivate() override;

    Result Mount(IAbstractedPad& abstracted_pad, u32 slot, NpadVibration* handler);
    Result Unmount();

    Result SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command);

    Result GetActualVibrationGcErmCommand(Core::HID::VibrationGcErmCommand& out_command);
    Result SendVibrationNotificationPattern(Core::HID::VibrationGcErmCommand command);

private:
    u32 adapter_slot;
};
} // namespace Service::HID


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

#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/vibration/n64_vibration_device.h"

namespace Service::HID {

NpadN64VibrationDevice::NpadN64VibrationDevice() {}

Result NpadN64VibrationDevice::Activate() {
    if (ref_counter == 0 && is_mounted) {
        f32 volume = 1.0f;
        const auto result = vibration_handler->GetVibrationVolume(volume);
        if (result.IsSuccess()) {
            xcd_handle->SetVibration(false);
        }
    }

    ref_counter++;
    return ResultSuccess;
}

Result NpadN64VibrationDevice::Deactivate() {
    if (ref_counter == 1 && is_mounted) {
        f32 volume = 1.0f;
        const auto result = vibration_handler->GetVibrationVolume(volume);
        if (result.IsSuccess()) {
            xcd_handle->SetVibration(false);
        }
    }

    if (ref_counter > 0) {
        ref_counter--;
    }

    return ResultSuccess;
}

Result NpadN64VibrationDevice::Mount(IAbstractedPad& abstracted_pad, NpadVibration* handler) {
    if (!abstracted_pad.internal_flags.is_connected) {
        return ResultSuccess;
    }
    xcd_handle = abstracted_pad.xcd_handle;
    vibration_handler = handler;
    is_mounted = true;

    if (ref_counter == 0) {
        return ResultSuccess;
    }

    f32 volume{1.0f};
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsSuccess()) {
        xcd_handle->SetVibration(false);
    }

    return ResultSuccess;
}

Result NpadN64VibrationDevice::Unmount() {
    if (ref_counter == 0 || !is_mounted) {
        is_mounted = false;
        return ResultSuccess;
    }

    f32 volume{1.0f};
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsSuccess()) {
        xcd_handle->SetVibration(false);
    }

    is_mounted = false;
    return ResultSuccess;
}

Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) {
    if (ref_counter < 1) {
        return ResultVibrationNotInitialized;
    }
    if (is_mounted) {
        f32 volume = 1.0f;
        const auto result = vibration_handler->GetVibrationVolume(volume);
        if (result.IsError()) {
            return result;
        }
        xcd_handle->SetVibration(false);
    }
    return ResultSuccess;
}

Result NpadN64VibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) {
    if (!is_mounted) {
        return ResultSuccess;
    }
    f32 volume = 1.0f;
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsError()) {
        return result;
    }
    if (volume <= 0.0) {
        pattern = 0;
    }
    // TODO: SendVibrationNotificationPattern
    return ResultSuccess;
}

} // namespace Service::HID


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

#pragma once

#include <array>
#include <mutex>

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

namespace Service::HID {
class NpadVibration;
struct IAbstractedPad;

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

    Result Activate() override;
    Result Deactivate() override;

    Result Mount(IAbstractedPad& abstracted_pad, NpadVibration* handler);
    Result Unmount();

    Result SendValueInBool(bool is_vibrating);
    Result SendVibrationNotificationPattern(u32 pattern);
};
} // namespace Service::HID


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

#include "hid_core/hid_result.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/vibration/vibration_base.h"

namespace Service::HID {

NpadVibrationBase::NpadVibrationBase() {}

Result NpadVibrationBase::Activate() {
    ref_counter++;
    return ResultSuccess;
}

Result NpadVibrationBase::Deactivate() {
    if (ref_counter > 0) {
        ref_counter--;
    }

    return ResultSuccess;
}

bool NpadVibrationBase::IsActive() const {
    return ref_counter > 0;
}

bool NpadVibrationBase::IsVibrationMounted() const {
    return is_mounted;
}

} // namespace Service::HID


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

#pragma once

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

namespace Core::HID {
class EmulatedController;
}

namespace Service::HID {
class NpadVibration;

/// Handles Npad request from HID interfaces
class NpadVibrationBase {
public:
    explicit NpadVibrationBase();

    virtual Result Activate();
    virtual Result Deactivate();

    bool IsActive() const;
    bool IsVibrationMounted() const;

protected:
    Core::HID::EmulatedController* xcd_handle{nullptr};
    s32 ref_counter{};
    bool is_mounted{};
    NpadVibration* vibration_handler{nullptr};
};
} // namespace Service::HID


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

#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/vibration/vibration_device.h"

namespace Service::HID {

NpadVibrationDevice::NpadVibrationDevice() {}

Result NpadVibrationDevice::Activate() {
    if (ref_counter == 0 && is_mounted) {
        f32 volume = 1.0f;
        const auto result = vibration_handler->GetVibrationVolume(volume);
        if (result.IsSuccess()) {
            xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
            // TODO: SendNotificationPattern;
        }
    }

    ref_counter++;
    return ResultSuccess;
}

Result NpadVibrationDevice::Deactivate() {
    if (ref_counter == 1 && is_mounted) {
        f32 volume = 1.0f;
        const auto result = vibration_handler->GetVibrationVolume(volume);
        if (result.IsSuccess()) {
            xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
            // TODO: SendNotificationPattern;
        }
    }

    if (ref_counter > 0) {
        ref_counter--;
    }

    return ResultSuccess;
}

Result NpadVibrationDevice::Mount(IAbstractedPad& abstracted_pad, Core::HID::DeviceIndex index,
                                  NpadVibration* handler) {
    if (!abstracted_pad.internal_flags.is_connected) {
        return ResultSuccess;
    }
    xcd_handle = abstracted_pad.xcd_handle;
    device_index = index;
    vibration_handler = handler;
    is_mounted = true;

    if (ref_counter == 0) {
        return ResultSuccess;
    }

    f32 volume{1.0f};
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsSuccess()) {
        xcd_handle->SetVibration(false);
    }

    return ResultSuccess;
}

Result NpadVibrationDevice::Unmount() {
    if (ref_counter == 0 || !is_mounted) {
        is_mounted = false;
        return ResultSuccess;
    }

    f32 volume{1.0f};
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsSuccess()) {
        xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
    }

    is_mounted = false;
    return ResultSuccess;
}

Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue& value) {
    if (ref_counter == 0) {
        return ResultVibrationNotInitialized;
    }
    if (!is_mounted) {
        return ResultSuccess;
    }

    f32 volume = 1.0f;
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsError()) {
        return result;
    }
    if (volume <= 0.0f) {
        xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
        return ResultSuccess;
    }

    Core::HID::VibrationValue vibration_value = value;
    vibration_value.high_amplitude *= volume;
    vibration_value.low_amplitude *= volume;

    xcd_handle->SetVibration(device_index, vibration_value);
    return ResultSuccess;
}

Result NpadVibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) {
    if (!is_mounted) {
        return ResultSuccess;
    }

    f32 volume = 1.0f;
    const auto result = vibration_handler->GetVibrationVolume(volume);
    if (result.IsError()) {
        return result;
    }
    if (volume <= 0.0) {
        pattern = 0;
    }

    // TODO: SendVibrationNotificationPattern;
    return ResultSuccess;
}

Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) const {
    if (ref_counter < 1) {
        return ResultVibrationNotInitialized;
    }

    out_value = Core::HID::DEFAULT_VIBRATION_VALUE;
    if (!is_mounted) {
        return ResultSuccess;
    }

    out_value = xcd_handle->GetActualVibrationValue(device_index);
    return ResultSuccess;
}

} // namespace Service::HID


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

#pragma once

#include <array>
#include <mutex>

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

namespace Core::HID {
enum class DeviceIndex : u8;
}

namespace Service::HID {
class NpadVibration;

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

    Result Activate();
    Result Deactivate();

    Result Mount(IAbstractedPad& abstracted_pad, Core::HID::DeviceIndex index,
                 NpadVibration* handler);
    Result Unmount();

    Result SendVibrationValue(const Core::HID::VibrationValue& value);
    Result SendVibrationNotificationPattern(u32 pattern);

    Result GetActualVibrationValue(Core::HID::VibrationValue& out_value) const;

private:
    Core::HID::DeviceIndex device_index{};
};

} // namespace Service::HID