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

#include <algorithm>
#include <fmt/format.h>

#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/frontend/input_converter.h"

namespace Core::HID {

EmulatedDevices::EmulatedDevices() = default;

EmulatedDevices::~EmulatedDevices() = default;

void EmulatedDevices::ReloadFromSettings() {
    ReloadInput();
}

void EmulatedDevices::ReloadInput() {
    // If you load any device here add the equivalent to the UnloadInput() function

    // Native Mouse is mapped on port 1, pad 0
    const Common::ParamPackage mouse_params{"engine:mouse,port:1,pad:0"};

    // Keyboard keys is mapped on port 1, pad 0 for normal keys, pad 1 for moddifier keys
    const Common::ParamPackage keyboard_params{"engine:keyboard,port:1"};

    std::size_t key_index = 0;
    for (auto& mouse_device : mouse_button_devices) {
        Common::ParamPackage mouse_button_params = mouse_params;
        mouse_button_params.Set("button", static_cast<int>(key_index));
        mouse_device = Common::Input::CreateInputDevice(mouse_button_params);
        key_index++;
    }

    Common::ParamPackage mouse_position_params = mouse_params;
    mouse_position_params.Set("axis_x", 0);
    mouse_position_params.Set("axis_y", 1);
    mouse_position_params.Set("deadzone", 0.0f);
    mouse_position_params.Set("range", 1.0f);
    mouse_position_params.Set("threshold", 0.0f);
    mouse_stick_device = Common::Input::CreateInputDevice(mouse_position_params);

    // First two axis are reserved for mouse position
    key_index = 2;
    for (auto& mouse_device : mouse_wheel_devices) {
        Common::ParamPackage mouse_wheel_params = mouse_params;
        mouse_wheel_params.Set("axis", static_cast<int>(key_index));
        mouse_device = Common::Input::CreateInputDevice(mouse_wheel_params);
        key_index++;
    }

    key_index = 0;
    for (auto& keyboard_device : keyboard_devices) {
        Common::ParamPackage keyboard_key_params = keyboard_params;
        keyboard_key_params.Set("button", static_cast<int>(key_index));
        keyboard_key_params.Set("pad", 0);
        keyboard_device = Common::Input::CreateInputDevice(keyboard_key_params);
        key_index++;
    }

    key_index = 0;
    for (auto& keyboard_device : keyboard_modifier_devices) {
        Common::ParamPackage keyboard_moddifier_params = keyboard_params;
        keyboard_moddifier_params.Set("button", static_cast<int>(key_index));
        keyboard_moddifier_params.Set("pad", 1);
        keyboard_device = Common::Input::CreateInputDevice(keyboard_moddifier_params);
        key_index++;
    }

    for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
        if (!mouse_button_devices[index]) {
            continue;
        }
        mouse_button_devices[index]->SetCallback({
            .on_change =
                [this, index](const Common::Input::CallbackStatus& callback) {
                    SetMouseButton(callback, index);
                },
        });
    }

    for (std::size_t index = 0; index < mouse_wheel_devices.size(); ++index) {
        if (!mouse_wheel_devices[index]) {
            continue;
        }
        mouse_wheel_devices[index]->SetCallback({
            .on_change =
                [this, index](const Common::Input::CallbackStatus& callback) {
                    SetMouseWheel(callback, index);
                },
        });
    }

    if (mouse_stick_device) {
        mouse_stick_device->SetCallback({
            .on_change =
                [this](const Common::Input::CallbackStatus& callback) {
                    SetMousePosition(callback);
                },
        });
    }

    for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
        if (!keyboard_devices[index]) {
            continue;
        }
        keyboard_devices[index]->SetCallback({
            .on_change =
                [this, index](const Common::Input::CallbackStatus& callback) {
                    SetKeyboardButton(callback, index);
                },
        });
    }

    for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
        if (!keyboard_modifier_devices[index]) {
            continue;
        }
        keyboard_modifier_devices[index]->SetCallback({
            .on_change =
                [this, index](const Common::Input::CallbackStatus& callback) {
                    SetKeyboardModifier(callback, index);
                },
        });
    }
}

void EmulatedDevices::UnloadInput() {
    for (auto& button : mouse_button_devices) {
        button.reset();
    }
    for (auto& analog : mouse_wheel_devices) {
        analog.reset();
    }
    mouse_stick_device.reset();
    for (auto& button : keyboard_devices) {
        button.reset();
    }
    for (auto& button : keyboard_modifier_devices) {
        button.reset();
    }
}

void EmulatedDevices::EnableConfiguration() {
    is_configuring = true;
    SaveCurrentConfig();
}

void EmulatedDevices::DisableConfiguration() {
    is_configuring = false;
}

bool EmulatedDevices::IsConfiguring() const {
    return is_configuring;
}

void EmulatedDevices::SaveCurrentConfig() {
    if (!is_configuring) {
        return;
    }
}

void EmulatedDevices::RestoreConfig() {
    if (!is_configuring) {
        return;
    }
    ReloadFromSettings();
}

void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
                                        std::size_t index) {
    if (index >= device_status.keyboard_values.size()) {
        return;
    }
    std::unique_lock lock{mutex};
    bool value_changed = false;
    const auto new_status = TransformToButton(callback);
    auto& current_status = device_status.keyboard_values[index];
    current_status.toggle = new_status.toggle;

    // Update button status with current status
    if (!current_status.toggle) {
        current_status.locked = false;
        if (current_status.value != new_status.value) {
            current_status.value = new_status.value;
            value_changed = true;
        }
    } else {
        // Toggle button and lock status
        if (new_status.value && !current_status.locked) {
            current_status.locked = true;
            current_status.value = !current_status.value;
            value_changed = true;
        }

        // Unlock button, ready for next press
        if (!new_status.value && current_status.locked) {
            current_status.locked = false;
        }
    }

    if (!value_changed) {
        return;
    }

    if (is_configuring) {
        lock.unlock();
        TriggerOnChange(DeviceTriggerType::Keyboard);
        return;
    }

    // Index should be converted from NativeKeyboard to KeyboardKeyIndex
    UpdateKey(index, current_status.value);

    lock.unlock();
    TriggerOnChange(DeviceTriggerType::Keyboard);
}

void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
    constexpr std::size_t KEYS_PER_BYTE = 8;
    auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE];
    const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE));
    if (status) {
        entry = entry | mask;
    } else {
        entry = static_cast<u8>(entry & ~mask);
    }
}

void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
                                          std::size_t index) {
    if (index >= device_status.keyboard_moddifier_values.size()) {
        return;
    }
    std::unique_lock lock{mutex};
    bool value_changed = false;
    const auto new_status = TransformToButton(callback);
    auto& current_status = device_status.keyboard_moddifier_values[index];
    current_status.toggle = new_status.toggle;

    // Update button status with current
    if (!current_status.toggle) {
        current_status.locked = false;
        if (current_status.value != new_status.value) {
            current_status.value = new_status.value;
            value_changed = true;
        }
    } else {
        // Toggle button and lock status
        if (new_status.value && !current_status.locked) {
            current_status.locked = true;
            current_status.value = !current_status.value;
            value_changed = true;
        }

        // Unlock button ready for next press
        if (!new_status.value && current_status.locked) {
            current_status.locked = false;
        }
    }

    if (!value_changed) {
        return;
    }

    if (is_configuring) {
        lock.unlock();
        TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
        return;
    }

    switch (index) {
    case Settings::NativeKeyboard::LeftControl:
    case Settings::NativeKeyboard::RightControl:
        device_status.keyboard_moddifier_state.control.Assign(current_status.value);
        break;
    case Settings::NativeKeyboard::LeftShift:
    case Settings::NativeKeyboard::RightShift:
        device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
        break;
    case Settings::NativeKeyboard::LeftAlt:
        device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
        break;
    case Settings::NativeKeyboard::RightAlt:
        device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
        break;
    case Settings::NativeKeyboard::CapsLock:
        device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
        break;
    case Settings::NativeKeyboard::ScrollLock:
        device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
        break;
    case Settings::NativeKeyboard::NumLock:
        device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
        break;
    }

    lock.unlock();
    TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
}

void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
                                     std::size_t index) {
    if (index >= device_status.mouse_button_values.size()) {
        return;
    }
    std::unique_lock lock{mutex};
    bool value_changed = false;
    const auto new_status = TransformToButton(callback);
    auto& current_status = device_status.mouse_button_values[index];
    current_status.toggle = new_status.toggle;

    // Update button status with current
    if (!current_status.toggle) {
        current_status.locked = false;
        if (current_status.value != new_status.value) {
            current_status.value = new_status.value;
            value_changed = true;
        }
    } else {
        // Toggle button and lock status
        if (new_status.value && !current_status.locked) {
            current_status.locked = true;
            current_status.value = !current_status.value;
            value_changed = true;
        }

        // Unlock button ready for next press
        if (!new_status.value && current_status.locked) {
            current_status.locked = false;
        }
    }

    if (!value_changed) {
        return;
    }

    if (is_configuring) {
        lock.unlock();
        TriggerOnChange(DeviceTriggerType::Mouse);
        return;
    }

    switch (index) {
    case Settings::NativeMouseButton::Left:
        device_status.mouse_button_state.left.Assign(current_status.value);
        break;
    case Settings::NativeMouseButton::Right:
        device_status.mouse_button_state.right.Assign(current_status.value);
        break;
    case Settings::NativeMouseButton::Middle:
        device_status.mouse_button_state.middle.Assign(current_status.value);
        break;
    case Settings::NativeMouseButton::Forward:
        device_status.mouse_button_state.forward.Assign(current_status.value);
        break;
    case Settings::NativeMouseButton::Back:
        device_status.mouse_button_state.back.Assign(current_status.value);
        break;
    }

    lock.unlock();
    TriggerOnChange(DeviceTriggerType::Mouse);
}

void EmulatedDevices::SetMouseWheel(const Common::Input::CallbackStatus& callback,
                                    std::size_t index) {
    if (index >= device_status.mouse_wheel_values.size()) {
        return;
    }
    std::unique_lock lock{mutex};
    const auto analog_value = TransformToAnalog(callback);

    device_status.mouse_wheel_values[index] = analog_value;

    if (is_configuring) {
        device_status.mouse_wheel_state = {};
        lock.unlock();
        TriggerOnChange(DeviceTriggerType::Mouse);
        return;
    }

    switch (index) {
    case Settings::NativeMouseWheel::X:
        device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value);
        break;
    case Settings::NativeMouseWheel::Y:
        device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value);
        break;
    }

    lock.unlock();
    TriggerOnChange(DeviceTriggerType::Mouse);
}

void EmulatedDevices::SetMousePosition(const Common::Input::CallbackStatus& callback) {
    std::unique_lock lock{mutex};
    const auto touch_value = TransformToTouch(callback);

    device_status.mouse_stick_value = touch_value;

    if (is_configuring) {
        device_status.mouse_position_state = {};
        lock.unlock();
        TriggerOnChange(DeviceTriggerType::Mouse);
        return;
    }

    device_status.mouse_position_state.x = touch_value.x.value;
    device_status.mouse_position_state.y = touch_value.y.value;

    lock.unlock();
    TriggerOnChange(DeviceTriggerType::Mouse);
}

KeyboardValues EmulatedDevices::GetKeyboardValues() const {
    std::scoped_lock lock{mutex};
    return device_status.keyboard_values;
}

KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
    std::scoped_lock lock{mutex};
    return device_status.keyboard_moddifier_values;
}

MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
    std::scoped_lock lock{mutex};
    return device_status.mouse_button_values;
}

KeyboardKey EmulatedDevices::GetKeyboard() const {
    std::scoped_lock lock{mutex};
    return device_status.keyboard_state;
}

KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
    std::scoped_lock lock{mutex};
    return device_status.keyboard_moddifier_state;
}

MouseButton EmulatedDevices::GetMouseButtons() const {
    std::scoped_lock lock{mutex};
    return device_status.mouse_button_state;
}

MousePosition EmulatedDevices::GetMousePosition() const {
    std::scoped_lock lock{mutex};
    return device_status.mouse_position_state;
}

AnalogStickState EmulatedDevices::GetMouseWheel() const {
    std::scoped_lock lock{mutex};
    return device_status.mouse_wheel_state;
}

void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
    std::scoped_lock lock{callback_mutex};
    for (const auto& poller_pair : callback_list) {
        const InterfaceUpdateCallback& poller = poller_pair.second;
        if (poller.on_change) {
            poller.on_change(type);
        }
    }
}

int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
    std::scoped_lock lock{callback_mutex};
    callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
    return last_callback_key++;
}

void EmulatedDevices::DeleteCallback(int key) {
    std::scoped_lock lock{callback_mutex};
    const auto& iterator = callback_list.find(key);
    if (iterator == callback_list.end()) {
        LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
        return;
    }
    callback_list.erase(iterator);
}
} // namespace Core::HID


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

#pragma once

#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>

#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/settings.h"
#include "hid_core/hid_types.h"

namespace Core::HID {
using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
                                   Settings::NativeKeyboard::NumKeyboardKeys>;
using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
                                           Settings::NativeKeyboard::NumKeyboardMods>;
using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
                                      Settings::NativeMouseButton::NumMouseButtons>;
using MouseWheelDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
                                     Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;

using MouseButtonParams =
    std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;

using KeyboardValues =
    std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
using KeyboardModifierValues =
    std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
using MouseButtonValues =
    std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
using MouseWheelValues =
    std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickValue = Common::Input::TouchStatus;

struct MousePosition {
    f32 x;
    f32 y;
};

struct DeviceStatus {
    // Data from input_common
    KeyboardValues keyboard_values{};
    KeyboardModifierValues keyboard_moddifier_values{};
    MouseButtonValues mouse_button_values{};
    MouseWheelValues mouse_wheel_values{};
    MouseStickValue mouse_stick_value{};

    // Data for HID services
    KeyboardKey keyboard_state{};
    KeyboardModifier keyboard_moddifier_state{};
    MouseButton mouse_button_state{};
    MousePosition mouse_position_state{};
    AnalogStickState mouse_wheel_state{};
};

enum class DeviceTriggerType {
    Keyboard,
    KeyboardModdifier,
    Mouse,
    RingController,
};

struct InterfaceUpdateCallback {
    std::function<void(DeviceTriggerType)> on_change;
};

class EmulatedDevices {
public:
    /**
     * Contains all input data related to external devices that aren't necessarily a controller
     * This includes devices such as the keyboard or mouse
     */
    explicit EmulatedDevices();
    ~EmulatedDevices();

    YUZU_NON_COPYABLE(EmulatedDevices);
    YUZU_NON_MOVEABLE(EmulatedDevices);

    /// Removes all callbacks created from input devices
    void UnloadInput();

    /**
     * Sets the emulated devices into configuring mode
     * This prevents the modification of the HID state of the emulated devices by input commands
     */
    void EnableConfiguration();

    /// Returns the emulated devices into normal mode, allowing the modification of the HID state
    void DisableConfiguration();

    /// Returns true if the emulated device is in configuring mode
    bool IsConfiguring() const;

    /// Reload all input devices
    void ReloadInput();

    /// Overrides current mapped devices with the stored configuration and reloads all input devices
    void ReloadFromSettings();

    /// Saves the current mapped configuration
    void SaveCurrentConfig();

    /// Reverts any mapped changes made that weren't saved
    void RestoreConfig();

    /// Returns the latest status of button input from the keyboard with parameters
    KeyboardValues GetKeyboardValues() const;

    /// Returns the latest status of button input from the keyboard modifiers with parameters
    KeyboardModifierValues GetKeyboardModdifierValues() const;

    /// Returns the latest status of button input from the mouse with parameters
    MouseButtonValues GetMouseButtonsValues() const;

    /// Returns the latest status of button input from the keyboard
    KeyboardKey GetKeyboard() const;

    /// Returns the latest status of button input from the keyboard modifiers
    KeyboardModifier GetKeyboardModifier() const;

    /// Returns the latest status of button input from the mouse
    MouseButton GetMouseButtons() const;

    /// Returns the latest mouse coordinates
    MousePosition GetMousePosition() const;

    /// Returns the latest mouse wheel change
    AnalogStickState GetMouseWheel() const;

    /**
     * Adds a callback to the list of events
     * @param update_callback InterfaceUpdateCallback that will be triggered
     * @return an unique key corresponding to the callback index in the list
     */
    int SetCallback(InterfaceUpdateCallback update_callback);

    /**
     * Removes a callback from the list stopping any future events to this object
     * @param key Key corresponding to the callback index in the list
     */
    void DeleteCallback(int key);

private:
    /// Helps assigning a value to keyboard_state
    void UpdateKey(std::size_t key_index, bool status);

    /**
     * Updates the touch status of the keyboard device
     * @param callback A CallbackStatus containing the key status
     * @param index key ID to be updated
     */
    void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);

    /**
     * Updates the keyboard status of the keyboard device
     * @param callback A CallbackStatus containing the modifier key status
     * @param index modifier key ID to be updated
     */
    void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);

    /**
     * Updates the mouse button status of the mouse device
     * @param callback A CallbackStatus containing the button status
     * @param index Button ID to be updated
     */
    void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);

    /**
     * Updates the mouse wheel status of the mouse device
     * @param callback A CallbackStatus containing the wheel status
     * @param index wheel ID to be updated
     */
    void SetMouseWheel(const Common::Input::CallbackStatus& callback, std::size_t index);

    /**
     * Updates the mouse position status of the mouse device
     * @param callback A CallbackStatus containing the position status
     */
    void SetMousePosition(const Common::Input::CallbackStatus& callback);

    /**
     * Triggers a callback that something has changed on the device status
     * @param type Input type of the event to trigger
     */
    void TriggerOnChange(DeviceTriggerType type);

    bool is_configuring{false};

    KeyboardDevices keyboard_devices;
    KeyboardModifierDevices keyboard_modifier_devices;
    MouseButtonDevices mouse_button_devices;
    MouseWheelDevices mouse_wheel_devices;
    MouseStickDevice mouse_stick_device;

    mutable std::mutex mutex;
    mutable std::mutex callback_mutex;
    std::unordered_map<int, InterfaceUpdateCallback> callback_list;
    int last_callback_key = 0;

    // Stores the current status of all external device input
    DeviceStatus device_status;
};

} // namespace Core::HID

repo_name: litucks/torzu

license: mit


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

#include <algorithm>
#include <random>

#include "common/input.h"
#include "hid_core/frontend/input_converter.h"

namespace Core::HID {

Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) {
    Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None};
    switch (callback.type) {
    case Common::Input::InputType::Analog:
    case Common::Input::InputType::Trigger: {
        const auto value = TransformToTrigger(callback).analog.value;
        battery = Common::Input::BatteryLevel::Empty;
        if (value > 0.2f) {
            battery = Common::Input::BatteryLevel::Critical;
        }
        if (value > 0.4f) {
            battery = Common::Input::BatteryLevel::Low;
        }
        if (value > 0.6f) {
            battery = Common::Input::BatteryLevel::Medium;
        }
        if (value > 0.8f) {
            battery = Common::Input::BatteryLevel::Full;
        }
        if (value >= 0.95f) {
            battery = Common::Input::BatteryLevel::Charging;
        }
        break;
    }
    case Common::Input::InputType::Button:
        battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging
                                               : Common::Input::BatteryLevel::Critical;
        break;
    case Common::Input::InputType::Battery:
        battery = callback.battery_status;
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type);
        break;
    }

    return battery;
}

Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) {
    Common::Input::ButtonStatus status{};
    switch (callback.type) {
    case Common::Input::InputType::Analog:
        status.value = TransformToTrigger(callback).pressed.value;
        status.toggle = callback.analog_status.properties.toggle;
        status.inverted = callback.analog_status.properties.inverted_button;
        break;
    case Common::Input::InputType::Trigger:
        status.value = TransformToTrigger(callback).pressed.value;
        break;
    case Common::Input::InputType::Button:
        status = callback.button_status;
        break;
    case Common::Input::InputType::Motion:
        status.value = std::abs(callback.motion_status.gyro.x.raw_value) > 1.0f;
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
        break;
    }

    if (status.inverted) {
        status.value = !status.value;
    }

    return status;
}

Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) {
    Common::Input::MotionStatus status{};
    switch (callback.type) {
    case Common::Input::InputType::Button: {
        Common::Input::AnalogProperties properties{
            .deadzone = 0.0f,
            .range = 1.0f,
            .offset = 0.0f,
        };
        status.delta_timestamp = 1000;
        status.force_update = true;
        status.accel.x = {
            .value = 0.0f,
            .raw_value = 0.0f,
            .properties = properties,
        };
        status.accel.y = {
            .value = 0.0f,
            .raw_value = 0.0f,
            .properties = properties,
        };
        status.accel.z = {
            .value = 0.0f,
            .raw_value = -1.0f,
            .properties = properties,
        };
        status.gyro.x = {
            .value = 0.0f,
            .raw_value = 0.0f,
            .properties = properties,
        };
        status.gyro.y = {
            .value = 0.0f,
            .raw_value = 0.0f,
            .properties = properties,
        };
        status.gyro.z = {
            .value = 0.0f,
            .raw_value = 0.0f,
            .properties = properties,
        };
        if (TransformToButton(callback).value) {
            std::random_device device;
            std::mt19937 gen(device());
            std::uniform_int_distribution<s16> distribution(-5000, 5000);
            status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
            status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
            status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
            status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
            status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
            status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
        }
        break;
    }
    case Common::Input::InputType::Motion:
        status = callback.motion_status;
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type);
        break;
    }
    SanitizeAnalog(status.accel.x, false);
    SanitizeAnalog(status.accel.y, false);
    SanitizeAnalog(status.accel.z, false);
    SanitizeAnalog(status.gyro.x, false);
    SanitizeAnalog(status.gyro.y, false);
    SanitizeAnalog(status.gyro.z, false);

    return status;
}

Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) {
    Common::Input::StickStatus status{};

    switch (callback.type) {
    case Common::Input::InputType::Stick:
        status = callback.stick_status;
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type);
        break;
    }

    SanitizeStick(status.x, status.y, true);
    const auto& properties_x = status.x.properties;
    const auto& properties_y = status.y.properties;
    const float x = status.x.value;
    const float y = status.y.value;

    // Set directional buttons
    status.right = x > properties_x.threshold;
    status.left = x < -properties_x.threshold;
    status.up = y > properties_y.threshold;
    status.down = y < -properties_y.threshold;

    return status;
}

Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) {
    Common::Input::TouchStatus status{};

    switch (callback.type) {
    case Common::Input::InputType::Touch:
        status = callback.touch_status;
        break;
    case Common::Input::InputType::Stick:
        status.x = callback.stick_status.x;
        status.y = callback.stick_status.y;
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type);
        break;
    }

    SanitizeAnalog(status.x, true);
    SanitizeAnalog(status.y, true);
    float& x = status.x.value;
    float& y = status.y.value;

    // Adjust if value is inverted
    x = status.x.properties.inverted ? 1.0f + x : x;
    y = status.y.properties.inverted ? 1.0f + y : y;

    // clamp value
    x = std::clamp(x, 0.0f, 1.0f);
    y = std::clamp(y, 0.0f, 1.0f);

    if (status.pressed.inverted) {
        status.pressed.value = !status.pressed.value;
    }

    return status;
}

Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) {
    Common::Input::TriggerStatus status{};
    float& raw_value = status.analog.raw_value;
    bool calculate_button_value = true;

    switch (callback.type) {
    case Common::Input::InputType::Analog:
        status.analog.properties = callback.analog_status.properties;
        raw_value = callback.analog_status.raw_value;
        break;
    case Common::Input::InputType::Button:
        status.analog.properties.range = 1.0f;
        status.analog.properties.inverted = callback.button_status.inverted;
        raw_value = callback.button_status.value ? 1.0f : 0.0f;
        break;
    case Common::Input::InputType::Trigger:
        status = callback.trigger_status;
        calculate_button_value = false;
        break;
    case Common::Input::InputType::Motion:
        status.analog.properties.range = 1.0f;
        raw_value = callback.motion_status.accel.x.raw_value;
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
        break;
    }

    SanitizeAnalog(status.analog, true);
    const auto& properties = status.analog.properties;
    float& value = status.analog.value;

    // Set button status
    if (calculate_button_value) {
        status.pressed.value = value > properties.threshold;
    }

    // Adjust if value is inverted
    value = properties.inverted ? 1.0f + value : value;

    // clamp value
    value = std::clamp(value, 0.0f, 1.0f);

    return status;
}

Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) {
    Common::Input::AnalogStatus status{};

    switch (callback.type) {
    case Common::Input::InputType::Analog:
        status.properties = callback.analog_status.properties;
        status.raw_value = callback.analog_status.raw_value;
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type);
        break;
    }

    SanitizeAnalog(status, false);

    // Adjust if value is inverted
    status.value = status.properties.inverted ? -status.value : status.value;

    return status;
}

Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
    Common::Input::CameraStatus camera{};
    switch (callback.type) {
    case Common::Input::InputType::IrSensor:
        camera = {
            .format = callback.camera_status,
            .data = callback.raw_data,
        };
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
        break;
    }

    return camera;
}

Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) {
    Common::Input::NfcStatus nfc{};
    switch (callback.type) {
    case Common::Input::InputType::Nfc:
        return callback.nfc_status;
    default:
        LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
        break;
    }

    return nfc;
}

Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) {
    switch (callback.type) {
    case Common::Input::InputType::Color:
        return callback.color_status;
        break;
    default:
        LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type);
        return {};
        break;
    }
}

void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
    const auto& properties = analog.properties;
    float& raw_value = analog.raw_value;
    float& value = analog.value;

    if (!std::isnormal(raw_value)) {
        raw_value = 0;
    }

    // Apply center offset
    raw_value -= properties.offset;

    // Set initial values to be formatted
    value = raw_value;

    // Calculate vector size
    const float r = std::abs(value);

    // Return zero if value is smaller than the deadzone
    if (r <= properties.deadzone || properties.deadzone == 1.0f) {
        analog.value = 0;
        return;
    }

    // Adjust range of value
    const float deadzone_factor =
        1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone);
    value = value * deadzone_factor / properties.range;

    // Invert direction if needed
    if (properties.inverted) {
        value = -value;
    }

    // Clamp value
    if (clamp_value) {
        value = std::clamp(value, -1.0f, 1.0f);
    }
}

void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
                   bool clamp_value) {
    const auto& properties_x = analog_x.properties;
    const auto& properties_y = analog_y.properties;
    float& raw_x = analog_x.raw_value;
    float& raw_y = analog_y.raw_value;
    float& x = analog_x.value;
    float& y = analog_y.value;

    if (!std::isnormal(raw_x)) {
        raw_x = 0;
    }
    if (!std::isnormal(raw_y)) {
        raw_y = 0;
    }

    // Apply center offset
    raw_x += properties_x.offset;
    raw_y += properties_y.offset;

    // Apply X scale correction from offset
    if (std::abs(properties_x.offset) < 0.75f) {
        if (raw_x > 0) {
            raw_x /= 1 + properties_x.offset;
        } else {
            raw_x /= 1 - properties_x.offset;
        }
    }

    // Apply Y scale correction from offset
    if (std::abs(properties_y.offset) < 0.75f) {
        if (raw_y > 0) {
            raw_y /= 1 + properties_y.offset;
        } else {
            raw_y /= 1 - properties_y.offset;
        }
    }

    // Invert direction if needed
    raw_x = properties_x.inverted ? -raw_x : raw_x;
    raw_y = properties_y.inverted ? -raw_y : raw_y;

    // Set initial values to be formatted
    x = raw_x;
    y = raw_y;

    // Calculate vector size
    float r = x * x + y * y;
    r = std::sqrt(r);

    // TODO(German77): Use deadzone and range of both axis

    // Return zero if values are smaller than the deadzone
    if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) {
        x = 0;
        y = 0;
        return;
    }

    // Adjust range of joystick
    const float deadzone_factor =
        1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone);
    x = x * deadzone_factor / properties_x.range;
    y = y * deadzone_factor / properties_x.range;
    r = r * deadzone_factor / properties_x.range;

    // Normalize joystick
    if (clamp_value && r > 1.0f) {
        x /= r;
        y /= r;
    }
}

} // namespace Core::HID


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

#pragma once

namespace Common::Input {
struct CallbackStatus;
enum class BatteryLevel : u32;
using BatteryStatus = BatteryLevel;
struct AnalogStatus;
struct ButtonStatus;
struct MotionStatus;
struct StickStatus;
struct TouchStatus;
struct TriggerStatus;
}; // namespace Common::Input

namespace Core::HID {

/**
 * Converts raw input data into a valid battery status.
 *
 * @param callback Supported callbacks: Analog, Battery, Trigger.
 * @return A valid BatteryStatus object.
 */
Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid button status. Applies invert properties to the output.
 *
 * @param callback Supported callbacks: Analog, Button, Trigger.
 * @return A valid TouchStatus object.
 */
Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid motion status.
 *
 * @param callback Supported callbacks: Motion.
 * @return A valid TouchStatus object.
 */
Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert
 * properties to the output.
 *
 * @param callback Supported callbacks: Stick.
 * @return A valid StickStatus object.
 */
Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid touch status.
 *
 * @param callback Supported callbacks: Touch.
 * @return A valid TouchStatus object.
 */
Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and
 * invert properties to the output. Button status uses the threshold property if necessary.
 *
 * @param callback Supported callbacks: Analog, Button, Trigger.
 * @return A valid TriggerStatus object.
 */
Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid analog status. Applies offset, deadzone, range and
 * invert properties to the output.
 *
 * @param callback Supported callbacks: Analog.
 * @return A valid AnalogStatus object.
 */
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid camera status.
 *
 * @param callback Supported callbacks: Camera.
 * @return A valid CameraObject object.
 */
Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid nfc status.
 *
 * @param callback Supported callbacks: Nfc.
 * @return A valid data tag vector.
 */
Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw input data into a valid color status.
 *
 * @param callback Supported callbacks: Color.
 * @return A valid Color object.
 */
Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback);

/**
 * Converts raw analog data into a valid analog value
 * @param analog An analog object containing raw data and properties
 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
 */
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value);

/**
 * Converts raw stick data into a valid stick value
 * @param analog_x raw analog data and properties for the x-axis
 * @param analog_y raw analog data and properties for the y-axis
 * @param clamp_value bool that determines if the value needs to be clamped into the unit circle.
 */
void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
                   bool clamp_value);

} // namespace Core::HID


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

#include "core/core.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/frontend/input_interpreter.h"
#include "hid_core/hid_types.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/npad/npad.h"

InputInterpreter::InputInterpreter(Core::System& system)
    : npad{system.ServiceManager()
               .GetService<Service::HID::IHidServer>("hid")
               ->GetResourceManager()
               ->GetNpad()} {
    ResetButtonStates();
}

InputInterpreter::~InputInterpreter() = default;

void InputInterpreter::PollInput() {
    if (npad == nullptr) {
        return;
    }
    const auto button_state = npad->GetAndResetPressState();

    previous_index = current_index;
    current_index = (current_index + 1) % button_states.size();

    button_states[current_index] = button_state;
}

void InputInterpreter::ResetButtonStates() {
    previous_index = 0;
    current_index = 0;

    button_states[0] = Core::HID::NpadButton::All;

    for (std::size_t i = 1; i < button_states.size(); ++i) {
        button_states[i] = Core::HID::NpadButton::None;
    }
}

bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
    return True(button_states[current_index] & button);
}

bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
    const bool current_press = True(button_states[current_index] & button);
    const bool previous_press = True(button_states[previous_index] & button);

    return current_press && !previous_press;
}

bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
    Core::HID::NpadButton held_buttons{button_states[0]};

    for (std::size_t i = 1; i < button_states.size(); ++i) {
        held_buttons &= button_states[i];
    }

    return True(held_buttons & button);
}


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

#pragma once

#include <array>

#include "common/common_types.h"

namespace Core {
class System;
}

namespace Core::HID {
enum class NpadButton : u64;
}

namespace Service::HID {
class NPad;
}

/**
 * The InputInterpreter class interfaces with HID to retrieve button press states.
 * Input is intended to be polled every 50ms so that a button is considered to be
 * held down after 400ms has elapsed since the initial button press and subsequent
 * repeated presses occur every 50ms.
 */
class InputInterpreter {
public:
    explicit InputInterpreter(Core::System& system);
    virtual ~InputInterpreter();

    /// Gets a button state from HID and inserts it into the array of button states.
    void PollInput();

    /// Resets all the button states to their defaults.
    void ResetButtonStates();

    /**
     * Checks whether the button is pressed.
     *
     * @param button The button to check.
     *
     * @returns True when the button is pressed.
     */
    [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const;

    /**
     * Checks whether any of the buttons in the parameter list is pressed.
     *
     * @tparam HIDButton The buttons to check.
     *
     * @returns True when at least one of the buttons is pressed.
     */
    template <Core::HID::NpadButton... T>
    [[nodiscard]] bool IsAnyButtonPressed() {
        return (IsButtonPressed(T) || ...);
    }

    /**
     * The specified button is considered to be pressed once
     * if it is currently pressed and not pressed previously.
     *
     * @param button The button to check.
     *
     * @returns True when the button is pressed once.
     */
    [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const;

    /**
     * Checks whether any of the buttons in the parameter list is pressed once.
     *
     * @tparam T The buttons to check.
     *
     * @returns True when at least one of the buttons is pressed once.
     */
    template <Core::HID::NpadButton... T>
    [[nodiscard]] bool IsAnyButtonPressedOnce() const {
        return (IsButtonPressedOnce(T) || ...);
    }

    /**
     * The specified button is considered to be held down if it is pressed in all 9 button states.
     *
     * @param button The button to check.
     *
     * @returns True when the button is held down.
     */
    [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const;

    /**
     * Checks whether any of the buttons in the parameter list is held down.
     *
     * @tparam T The buttons to check.
     *
     * @returns True when at least one of the buttons is held down.
     */
    template <Core::HID::NpadButton... T>
    [[nodiscard]] bool IsAnyButtonHeld() const {
        return (IsButtonHeld(T) || ...);
    }

private:
    std::shared_ptr<Service::HID::NPad> npad;

    /// Stores 9 consecutive button states polled from HID.
    std::array<Core::HID::NpadButton, 9> button_states{};

    std::size_t previous_index{};
    std::size_t current_index{};
};


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

#include <cmath>

#include "common/math_util.h"
#include "hid_core/frontend/motion_input.h"

namespace Core::HID {

MotionInput::MotionInput() {
    // Initialize PID constants with default values
    SetPID(0.3f, 0.005f, 0.0f);
    SetGyroThreshold(ThresholdStandard);
    ResetQuaternion();
    ResetRotations();
}

void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
    kp = new_kp;
    ki = new_ki;
    kd = new_kd;
}

void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
    accel = acceleration;

    accel.x = std::clamp(accel.x, -AccelMaxValue, AccelMaxValue);
    accel.y = std::clamp(accel.y, -AccelMaxValue, AccelMaxValue);
    accel.z = std::clamp(accel.z, -AccelMaxValue, AccelMaxValue);
}

void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
    gyro = gyroscope - gyro_bias;

    gyro.x = std::clamp(gyro.x, -GyroMaxValue, GyroMaxValue);
    gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue);
    gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue);

    // Auto adjust gyro_bias to minimize drift
    if (!IsMoving(IsAtRestRelaxed)) {
        gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
    }

    // Adjust drift when calibration mode is enabled
    if (calibration_mode) {
        gyro_bias = (gyro_bias * 0.99f) + (gyroscope * 0.01f);
        StopCalibration();
    }

    if (gyro.Length() < gyro_threshold * user_gyro_threshold) {
        gyro = {};
    } else {
        only_accelerometer = false;
    }
}

void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
    quat = quaternion;
}

void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) {
    const float cr = std::cos(euler_angles.x * 0.5f);
    const float sr = std::sin(euler_angles.x * 0.5f);
    const float cp = std::cos(euler_angles.y * 0.5f);
    const float sp = std::sin(euler_angles.y * 0.5f);
    const float cy = std::cos(euler_angles.z * 0.5f);
    const float sy = std::sin(euler_angles.z * 0.5f);

    quat.w = cr * cp * cy + sr * sp * sy;
    quat.xyz.x = sr * cp * cy - cr * sp * sy;
    quat.xyz.y = cr * sp * cy + sr * cp * sy;
    quat.xyz.z = cr * cp * sy - sr * sp * cy;
}

void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
    gyro_bias = bias;
}

void MotionInput::SetGyroThreshold(f32 threshold) {
    gyro_threshold = threshold;
}

void MotionInput::SetUserGyroThreshold(f32 threshold) {
    user_gyro_threshold = threshold / ThresholdStandard;
}

void MotionInput::EnableReset(bool reset) {
    reset_enabled = reset;
}

void MotionInput::ResetRotations() {
    rotations = {};
}

void MotionInput::ResetQuaternion() {
    quat = {{0.0f, 0.0f, -1.0f}, 0.0f};
}

bool MotionInput::IsMoving(f32 sensitivity) const {
    return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f;
}

bool MotionInput::IsCalibrated(f32 sensitivity) const {
    return real_error.Length() < sensitivity;
}

void MotionInput::UpdateRotation(u64 elapsed_time) {
    const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
    if (sample_period > 0.1f) {
        return;
    }
    rotations += gyro * sample_period;
}

void MotionInput::Calibrate() {
    calibration_mode = true;
    calibration_counter = 0;
}

void MotionInput::StopCalibration() {
    if (calibration_counter++ > CalibrationSamples) {
        calibration_mode = false;
        ResetQuaternion();
        ResetRotations();
    }
}

// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
void MotionInput::UpdateOrientation(u64 elapsed_time) {
    if (!IsCalibrated(0.1f)) {
        ResetOrientation();
    }
    // Short name local variable for readability
    f32 q1 = quat.w;
    f32 q2 = quat.xyz[0];
    f32 q3 = quat.xyz[1];
    f32 q4 = quat.xyz[2];
    const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;

    // Ignore invalid elapsed time
    if (sample_period > 0.1f) {
        return;
    }

    const auto normal_accel = accel.Normalized();
    auto rad_gyro = gyro * Common::PI * 2;
    const f32 swap = rad_gyro.x;
    rad_gyro.x = rad_gyro.y;
    rad_gyro.y = -swap;
    rad_gyro.z = -rad_gyro.z;

    // Clear gyro values if there is no gyro present
    if (only_accelerometer) {
        rad_gyro.x = 0;
        rad_gyro.y = 0;
        rad_gyro.z = 0;
    }

    // Ignore drift correction if acceleration is not reliable
    if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
        const f32 ax = -normal_accel.x;
        const f32 ay = normal_accel.y;
        const f32 az = -normal_accel.z;

        // Estimated direction of gravity
        const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
        const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
        const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;

        // Error is cross product between estimated direction and measured direction of gravity
        const Common::Vec3f new_real_error = {
            az * vx - ax * vz,
            ay * vz - az * vy,
            ax * vy - ay * vx,
        };

        derivative_error = new_real_error - real_error;
        real_error = new_real_error;

        // Prevent integral windup
        if (ki != 0.0f && !IsCalibrated(0.05f)) {
            integral_error += real_error;
        } else {
            integral_error = {};
        }

        // Apply feedback terms
        if (!only_accelerometer) {
            rad_gyro += kp * real_error;
            rad_gyro += ki * integral_error;
            rad_gyro += kd * derivative_error;
        } else {
            // Give more weight to accelerometer values to compensate for the lack of gyro
            rad_gyro += 35.0f * kp * real_error;
            rad_gyro += 10.0f * ki * integral_error;
            rad_gyro += 10.0f * kd * derivative_error;

            // Emulate gyro values for games that need them
            gyro.x = -rad_gyro.y;
            gyro.y = rad_gyro.x;
            gyro.z = -rad_gyro.z;
            UpdateRotation(elapsed_time);
        }
    }

    const f32 gx = rad_gyro.y;
    const f32 gy = rad_gyro.x;
    const f32 gz = rad_gyro.z;

    // Integrate rate of change of quaternion
    const f32 pa = q2;
    const f32 pb = q3;
    const f32 pc = q4;
    q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
    q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
    q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
    q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);

    quat.w = q1;
    quat.xyz[0] = q2;
    quat.xyz[1] = q3;
    quat.xyz[2] = q4;
    quat = quat.Normalized();
}

std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const {
    const Common::Quaternion<float> quad{
        .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w},
        .w = -quat.xyz[2],
    };
    const std::array<float, 16> matrix4x4 = quad.ToMatrix();

    return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
            Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
            Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
}

Common::Vec3f MotionInput::GetAcceleration() const {
    return accel;
}

Common::Vec3f MotionInput::GetGyroscope() const {
    return gyro;
}

Common::Vec3f MotionInput::GetGyroBias() const {
    return gyro_bias;
}

Common::Quaternion<f32> MotionInput::GetQuaternion() const {
    return quat;
}

Common::Vec3f MotionInput::GetRotations() const {
    return rotations;
}

Common::Vec3f MotionInput::GetEulerAngles() const {
    // roll (x-axis rotation)
    const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z);
    const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y);

    // pitch (y-axis rotation)
    const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
    const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));

    // yaw (z-axis rotation)
    const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y);
    const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z);

    return {
        std::atan2(sinr_cosp, cosr_cosp),
        2 * std::atan2(sinp, cosp) - Common::PI / 2,
        std::atan2(siny_cosp, cosy_cosp),
    };
}

void MotionInput::ResetOrientation() {
    if (!reset_enabled || only_accelerometer) {
        return;
    }
    if (!IsMoving(IsAtRestRelaxed) && accel.z <= -0.9f) {
        ++reset_counter;
        if (reset_counter > 900) {
            quat.w = 0;
            quat.xyz[0] = 0;
            quat.xyz[1] = 0;
            quat.xyz[2] = -1;
            SetOrientationFromAccelerometer();
            integral_error = {};
            reset_counter = 0;
        }
    } else {
        reset_counter = 0;
    }
}

void MotionInput::SetOrientationFromAccelerometer() {
    int iterations = 0;
    const f32 sample_period = 0.015f;

    const auto normal_accel = accel.Normalized();

    while (!IsCalibrated(0.01f) && ++iterations < 100) {
        // Short name local variable for readability
        f32 q1 = quat.w;
        f32 q2 = quat.xyz[0];
        f32 q3 = quat.xyz[1];
        f32 q4 = quat.xyz[2];

        Common::Vec3f rad_gyro;
        const f32 ax = -normal_accel.x;
        const f32 ay = normal_accel.y;
        const f32 az = -normal_accel.z;

        // Estimated direction of gravity
        const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
        const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
        const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;

        // Error is cross product between estimated direction and measured direction of gravity
        const Common::Vec3f new_real_error = {
            az * vx - ax * vz,
            ay * vz - az * vy,
            ax * vy - ay * vx,
        };

        derivative_error = new_real_error - real_error;
        real_error = new_real_error;

        rad_gyro += 10.0f * kp * real_error;
        rad_gyro += 5.0f * ki * integral_error;
        rad_gyro += 10.0f * kd * derivative_error;

        const f32 gx = rad_gyro.y;
        const f32 gy = rad_gyro.x;
        const f32 gz = rad_gyro.z;

        // Integrate rate of change of quaternion
        const f32 pa = q2;
        const f32 pb = q3;
        const f32 pc = q4;
        q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
        q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
        q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
        q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);

        quat.w = q1;
        quat.xyz[0] = q2;
        quat.xyz[1] = q3;
        quat.xyz[2] = q4;
        quat = quat.Normalized();
    }
}
} // namespace Core::HID


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

#pragma once

#include "common/common_types.h"
#include "common/quaternion.h"
#include "common/vector_math.h"

namespace Core::HID {

class MotionInput {
public:
    static constexpr float ThresholdLoose = 0.01f;
    static constexpr float ThresholdStandard = 0.007f;
    static constexpr float ThresholdTight = 0.002f;

    static constexpr float IsAtRestRelaxed = 0.05f;
    static constexpr float IsAtRestLoose = 0.02f;
    static constexpr float IsAtRestStandard = 0.01f;
    static constexpr float IsAtRestTight = 0.005f;

    static constexpr float GyroMaxValue = 5.0f;
    static constexpr float AccelMaxValue = 7.0f;

    static constexpr std::size_t CalibrationSamples = 300;

    explicit MotionInput();

    MotionInput(const MotionInput&) = default;
    MotionInput& operator=(const MotionInput&) = default;

    MotionInput(MotionInput&&) = default;
    MotionInput& operator=(MotionInput&&) = default;

    void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
    void SetAcceleration(const Common::Vec3f& acceleration);
    void SetGyroscope(const Common::Vec3f& gyroscope);
    void SetQuaternion(const Common::Quaternion<f32>& quaternion);
    void SetEulerAngles(const Common::Vec3f& euler_angles);
    void SetGyroBias(const Common::Vec3f& bias);
    void SetGyroThreshold(f32 threshold);

    /// Applies a modifier on top of the normal gyro threshold
    void SetUserGyroThreshold(f32 threshold);

    void EnableReset(bool reset);
    void ResetRotations();
    void ResetQuaternion();

    void UpdateRotation(u64 elapsed_time);
    void UpdateOrientation(u64 elapsed_time);

    void Calibrate();

    [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
    [[nodiscard]] Common::Vec3f GetAcceleration() const;
    [[nodiscard]] Common::Vec3f GetGyroscope() const;
    [[nodiscard]] Common::Vec3f GetGyroBias() const;
    [[nodiscard]] Common::Vec3f GetRotations() const;
    [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
    [[nodiscard]] Common::Vec3f GetEulerAngles() const;

    [[nodiscard]] bool IsMoving(f32 sensitivity) const;
    [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;

private:
    void StopCalibration();
    void ResetOrientation();
    void SetOrientationFromAccelerometer();

    // PID constants
    f32 kp;
    f32 ki;
    f32 kd;

    // PID errors
    Common::Vec3f real_error;
    Common::Vec3f integral_error;
    Common::Vec3f derivative_error;

    // Quaternion containing the device orientation
    Common::Quaternion<f32> quat;

    // Number of full rotations in each axis
    Common::Vec3f rotations;

    // Acceleration vector measurement in G force
    Common::Vec3f accel;

    // Gyroscope vector measurement in radians/s.
    Common::Vec3f gyro;

    // Vector to be subtracted from gyro measurements
    Common::Vec3f gyro_bias;

    // Minimum gyro amplitude to detect if the device is moving
    f32 gyro_threshold = 0.0f;

    // Multiplies gyro_threshold by this value
    f32 user_gyro_threshold = 0.0f;

    // Number of invalid sequential data
    u32 reset_counter = 0;

    // If the provided data is invalid the device will be autocalibrated
    bool reset_enabled = true;

    // Use accelerometer values to calculate position
    bool only_accelerometer = true;

    // When enabled it will aggressively adjust for gyro drift
    bool calibration_mode = false;

    // Used to auto disable calibration mode
    std::size_t calibration_counter = 0;
};

} // namespace Core::HID


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

#include "common/assert.h"
#include "hid_core/frontend/emulated_console.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_util.h"

namespace Core::HID {

HIDCore::HIDCore()
    : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
      player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
      player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
      player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
      player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
      player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
      player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
      player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
      other{std::make_unique<EmulatedController>(NpadIdType::Other)},
      handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
      console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}

HIDCore::~HIDCore() = default;

EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
    switch (npad_id_type) {
    case NpadIdType::Player1:
        return player_1.get();
    case NpadIdType::Player2:
        return player_2.get();
    case NpadIdType::Player3:
        return player_3.get();
    case NpadIdType::Player4:
        return player_4.get();
    case NpadIdType::Player5:
        return player_5.get();
    case NpadIdType::Player6:
        return player_6.get();
    case NpadIdType::Player7:
        return player_7.get();
    case NpadIdType::Player8:
        return player_8.get();
    case NpadIdType::Other:
        return other.get();
    case NpadIdType::Handheld:
        return handheld.get();
    case NpadIdType::Invalid:
    default:
        ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
        return nullptr;
    }
}

const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
    switch (npad_id_type) {
    case NpadIdType::Player1:
        return player_1.get();
    case NpadIdType::Player2:
        return player_2.get();
    case NpadIdType::Player3:
        return player_3.get();
    case NpadIdType::Player4:
        return player_4.get();
    case NpadIdType::Player5:
        return player_5.get();
    case NpadIdType::Player6:
        return player_6.get();
    case NpadIdType::Player7:
        return player_7.get();
    case NpadIdType::Player8:
        return player_8.get();
    case NpadIdType::Other:
        return other.get();
    case NpadIdType::Handheld:
        return handheld.get();
    case NpadIdType::Invalid:
    default:
        ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
        return nullptr;
    }
}
EmulatedConsole* HIDCore::GetEmulatedConsole() {
    return console.get();
}

const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
    return console.get();
}

EmulatedDevices* HIDCore::GetEmulatedDevices() {
    return devices.get();
}

const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
    return devices.get();
}

EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
    return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
}

const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
    return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
}

void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
    supported_style_tag.raw = style_tag.raw;
    player_1->SetSupportedNpadStyleTag(supported_style_tag);
    player_2->SetSupportedNpadStyleTag(supported_style_tag);
    player_3->SetSupportedNpadStyleTag(supported_style_tag);
    player_4->SetSupportedNpadStyleTag(supported_style_tag);
    player_5->SetSupportedNpadStyleTag(supported_style_tag);
    player_6->SetSupportedNpadStyleTag(supported_style_tag);
    player_7->SetSupportedNpadStyleTag(supported_style_tag);
    player_8->SetSupportedNpadStyleTag(supported_style_tag);
    other->SetSupportedNpadStyleTag(supported_style_tag);
    handheld->SetSupportedNpadStyleTag(supported_style_tag);
}

NpadStyleTag HIDCore::GetSupportedStyleTag() const {
    return supported_style_tag;
}

s8 HIDCore::GetPlayerCount() const {
    s8 active_players = 0;
    for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) {
        const auto* const controller = GetEmulatedControllerByIndex(player_index);
        if (controller->IsConnected()) {
            active_players++;
        }
    }
    return active_players;
}

NpadIdType HIDCore::GetFirstNpadId() const {
    for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
        const auto* const controller = GetEmulatedControllerByIndex(player_index);
        if (controller->IsConnected()) {
            return controller->GetNpadIdType();
        }
    }
    return NpadIdType::Player1;
}

NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
    for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
        const auto* const controller = GetEmulatedControllerByIndex(player_index);
        if (!controller->IsConnected()) {
            return controller->GetNpadIdType();
        }
    }
    return NpadIdType::Player1;
}

void HIDCore::SetLastActiveController(NpadIdType npad_id) {
    last_active_controller = npad_id;
}

NpadIdType HIDCore::GetLastActiveController() const {
    return last_active_controller;
}

void HIDCore::EnableAllControllerConfiguration() {
    player_1->EnableConfiguration();
    player_2->EnableConfiguration();
    player_3->EnableConfiguration();
    player_4->EnableConfiguration();
    player_5->EnableConfiguration();
    player_6->EnableConfiguration();
    player_7->EnableConfiguration();
    player_8->EnableConfiguration();
    other->EnableConfiguration();
    handheld->EnableConfiguration();
}

void HIDCore::DisableAllControllerConfiguration() {
    player_1->DisableConfiguration();
    player_2->DisableConfiguration();
    player_3->DisableConfiguration();
    player_4->DisableConfiguration();
    player_5->DisableConfiguration();
    player_6->DisableConfiguration();
    player_7->DisableConfiguration();
    player_8->DisableConfiguration();
    other->DisableConfiguration();
    handheld->DisableConfiguration();
}

void HIDCore::ReloadInputDevices() {
    player_1->ReloadFromSettings();
    player_2->ReloadFromSettings();
    player_3->ReloadFromSettings();
    player_4->ReloadFromSettings();
    player_5->ReloadFromSettings();
    player_6->ReloadFromSettings();
    player_7->ReloadFromSettings();
    player_8->ReloadFromSettings();
    other->ReloadFromSettings();
    handheld->ReloadFromSettings();
    console->ReloadFromSettings();
    devices->ReloadFromSettings();
}

void HIDCore::UnloadInputDevices() {
    player_1->UnloadInput();
    player_2->UnloadInput();
    player_3->UnloadInput();
    player_4->UnloadInput();
    player_5->UnloadInput();
    player_6->UnloadInput();
    player_7->UnloadInput();
    player_8->UnloadInput();
    other->UnloadInput();
    handheld->UnloadInput();
    console->UnloadInput();
    devices->UnloadInput();
}

} // namespace Core::HID


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

#pragma once

#include <memory>

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

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

namespace Core::HID {

class HIDCore {
public:
    explicit HIDCore();
    ~HIDCore();

    YUZU_NON_COPYABLE(HIDCore);
    YUZU_NON_MOVEABLE(HIDCore);

    EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
    const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;

    EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
    const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;

    EmulatedConsole* GetEmulatedConsole();
    const EmulatedConsole* GetEmulatedConsole() const;

    EmulatedDevices* GetEmulatedDevices();
    const EmulatedDevices* GetEmulatedDevices() const;

    void SetSupportedStyleTag(NpadStyleTag style_tag);
    NpadStyleTag GetSupportedStyleTag() const;

    /// Counts the connected players from P1-P8
    s8 GetPlayerCount() const;

    /// Returns the first connected npad id
    NpadIdType GetFirstNpadId() const;

    /// Returns the first disconnected npad id
    NpadIdType GetFirstDisconnectedNpadId() const;

    /// Sets the npad id of the last active controller
    void SetLastActiveController(NpadIdType npad_id);

    /// Returns the npad id of the last controller that pushed a button
    NpadIdType GetLastActiveController() const;

    /// Sets all emulated controllers into configuring mode.
    void EnableAllControllerConfiguration();

    /// Sets all emulated controllers into normal mode.
    void DisableAllControllerConfiguration();

    /// Reloads all input devices from settings
    void ReloadInputDevices();

    /// Removes all callbacks from input common
    void UnloadInputDevices();

    /// Number of emulated controllers
    static constexpr std::size_t available_controllers{10};

private:
    std::unique_ptr<EmulatedController> player_1;
    std::unique_ptr<EmulatedController> player_2;
    std::unique_ptr<EmulatedController> player_3;
    std::unique_ptr<EmulatedController> player_4;
    std::unique_ptr<EmulatedController> player_5;
    std::unique_ptr<EmulatedController> player_6;
    std::unique_ptr<EmulatedController> player_7;
    std::unique_ptr<EmulatedController> player_8;
    std::unique_ptr<EmulatedController> other;
    std::unique_ptr<EmulatedController> handheld;
    std::unique_ptr<EmulatedConsole> console;
    std::unique_ptr<EmulatedDevices> devices;
    NpadStyleTag supported_style_tag{NpadStyleSet::All};
    NpadIdType last_active_controller{NpadIdType::Handheld};
};

} // namespace Core::HID


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

#pragma once

#include "core/hle/result.h"

namespace Service::HID {

constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};

constexpr Result ResultTouchNotInitialized{ErrorModule::HID, 41};
constexpr Result ResultTouchOverflow{ErrorModule::HID, 42};

constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};

constexpr Result ResultVibrationNotInitialized{ErrorModule::HID, 121};
constexpr Result ResultVibrationInvalidStyleIndex{ErrorModule::HID, 122};
constexpr Result ResultVibrationInvalidNpadId{ErrorModule::HID, 123};
constexpr Result ResultVibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
constexpr Result ResultVibrationStrengthOutOfRange{ErrorModule::HID, 126};
constexpr Result ResultVibrationArraySizeMismatch{ErrorModule::HID, 131};

constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};

constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461};
constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464};
constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501};

constexpr Result ResultGestureOverflow{ErrorModule::HID, 522};
constexpr Result ResultGestureNotInitialized{ErrorModule::HID, 523};

constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541};

constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
constexpr Result NpadIsSameType{ErrorModule::HID, 602};
constexpr Result ResultNpadIsNotProController{ErrorModule::HID, 604};

constexpr Result ResultInvalidNpadId{ErrorModule::HID, 709};
constexpr Result ResultNpadNotConnected{ErrorModule::HID, 710};
constexpr Result ResultNpadHandlerOverflow{ErrorModule::HID, 711};
constexpr Result ResultNpadHandlerNotInitialized{ErrorModule::HID, 712};
constexpr Result ResultInvalidArraySize{ErrorModule::HID, 715};
constexpr Result ResultUndefinedStyleset{ErrorModule::HID, 716};
constexpr Result ResultMultipleStyleSetSelected{ErrorModule::HID, 717};

constexpr Result ResultAppletResourceOverflow{ErrorModule::HID, 1041};
constexpr Result ResultAppletResourceNotInitialized{ErrorModule::HID, 1042};
constexpr Result ResultSharedMemoryNotInitialized{ErrorModule::HID, 1043};
constexpr Result ResultAruidNoAvailableEntries{ErrorModule::HID, 1044};
constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046};
constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047};

constexpr Result ResultNpadResourceOverflow{ErrorModule::HID, 2001};
constexpr Result ResultNpadResourceNotInitialized{ErrorModule::HID, 2002};

constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};

} // namespace Service::HID

namespace Service::IRS {

constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};

} // namespace Service::IRS


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

#pragma once

#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/point.h"
#include "common/uuid.h"
#include "common/vector_math.h"

namespace Core::HID {

enum class DeviceIndex : u8 {
    Left = 0,
    Right = 1,
    None = 2,
    MaxDeviceIndex = 3,
};

// This is nn::hid::NpadButton
enum class NpadButton : u64 {
    None = 0,
    A = 1U << 0,
    B = 1U << 1,
    X = 1U << 2,
    Y = 1U << 3,
    StickL = 1U << 4,
    StickR = 1U << 5,
    L = 1U << 6,
    R = 1U << 7,
    ZL = 1U << 8,
    ZR = 1U << 9,
    Plus = 1U << 10,
    Minus = 1U << 11,

    Left = 1U << 12,
    Up = 1U << 13,
    Right = 1U << 14,
    Down = 1U << 15,

    StickLLeft = 1U << 16,
    StickLUp = 1U << 17,
    StickLRight = 1U << 18,
    StickLDown = 1U << 19,

    StickRLeft = 1U << 20,
    StickRUp = 1U << 21,
    StickRRight = 1U << 22,
    StickRDown = 1U << 23,

    LeftSL = 1U << 24,
    LeftSR = 1U << 25,

    RightSL = 1U << 26,
    RightSR = 1U << 27,

    Palma = 1U << 28,
    Verification = 1U << 29,
    HandheldLeftB = 1U << 30,
    LagonCLeft = 1U << 31,
    LagonCUp = 1ULL << 32,
    LagonCRight = 1ULL << 33,
    LagonCDown = 1ULL << 34,

    All = 0xFFFFFFFFFFFFFFFFULL,
};
DECLARE_ENUM_FLAG_OPERATORS(NpadButton);

enum class KeyboardKeyIndex : u32 {
    A = 4,
    B = 5,
    C = 6,
    D = 7,
    E = 8,
    F = 9,
    G = 10,
    H = 11,
    I = 12,
    J = 13,
    K = 14,
    L = 15,
    M = 16,
    N = 17,
    O = 18,
    P = 19,
    Q = 20,
    R = 21,
    S = 22,
    T = 23,
    U = 24,
    V = 25,
    W = 26,
    X = 27,
    Y = 28,
    Z = 29,
    D1 = 30,
    D2 = 31,
    D3 = 32,
    D4 = 33,
    D5 = 34,
    D6 = 35,
    D7 = 36,
    D8 = 37,
    D9 = 38,
    D0 = 39,
    Return = 40,
    Escape = 41,
    Backspace = 42,
    Tab = 43,
    Space = 44,
    Minus = 45,
    Plus = 46,
    OpenBracket = 47,
    CloseBracket = 48,
    Pipe = 49,
    Tilde = 50,
    Semicolon = 51,
    Quote = 52,
    Backquote = 53,
    Comma = 54,
    Period = 55,
    Slash = 56,
    CapsLock = 57,
    F1 = 58,
    F2 = 59,
    F3 = 60,
    F4 = 61,
    F5 = 62,
    F6 = 63,
    F7 = 64,
    F8 = 65,
    F9 = 66,
    F10 = 67,
    F11 = 68,
    F12 = 69,
    PrintScreen = 70,
    ScrollLock = 71,
    Pause = 72,
    Insert = 73,
    Home = 74,
    PageUp = 75,
    Delete = 76,
    End = 77,
    PageDown = 78,
    RightArrow = 79,
    LeftArrow = 80,
    DownArrow = 81,
    UpArrow = 82,
    NumLock = 83,
    NumPadDivide = 84,
    NumPadMultiply = 85,
    NumPadSubtract = 86,
    NumPadAdd = 87,
    NumPadEnter = 88,
    NumPad1 = 89,
    NumPad2 = 90,
    NumPad3 = 91,
    NumPad4 = 92,
    NumPad5 = 93,
    NumPad6 = 94,
    NumPad7 = 95,
    NumPad8 = 96,
    NumPad9 = 97,
    NumPad0 = 98,
    NumPadDot = 99,
    Backslash = 100,
    Application = 101,
    Power = 102,
    NumPadEquals = 103,
    F13 = 104,
    F14 = 105,
    F15 = 106,
    F16 = 107,
    F17 = 108,
    F18 = 109,
    F19 = 110,
    F20 = 111,
    F21 = 112,
    F22 = 113,
    F23 = 114,
    F24 = 115,
    NumPadComma = 133,
    Ro = 135,
    KatakanaHiragana = 136,
    Yen = 137,
    Henkan = 138,
    Muhenkan = 139,
    NumPadCommaPc98 = 140,
    HangulEnglish = 144,
    Hanja = 145,
    Katakana = 146,
    Hiragana = 147,
    ZenkakuHankaku = 148,
    LeftControl = 224,
    LeftShift = 225,
    LeftAlt = 226,
    LeftGui = 227,
    RightControl = 228,
    RightShift = 229,
    RightAlt = 230,
    RightGui = 231,
};

// This is nn::hid::NpadIdType
enum class NpadIdType : u32 {
    Player1 = 0x0,
    Player2 = 0x1,
    Player3 = 0x2,
    Player4 = 0x3,
    Player5 = 0x4,
    Player6 = 0x5,
    Player7 = 0x6,
    Player8 = 0x7,
    Other = 0x10,
    Handheld = 0x20,

    Invalid = 0xFFFFFFFF,
};

enum class NpadInterfaceType : u8 {
    None = 0,
    Bluetooth = 1,
    Rail = 2,
    Usb = 3,
    Embedded = 4,
};

// This is nn::hid::NpadStyleIndex
enum class NpadStyleIndex : u8 {
    None = 0,
    Fullkey = 3,
    Handheld = 4,
    HandheldNES = 4,
    JoyconDual = 5,
    JoyconLeft = 6,
    JoyconRight = 7,
    GameCube = 8,
    Pokeball = 9,
    NES = 10,
    SNES = 12,
    N64 = 13,
    SegaGenesis = 14,
    SystemExt = 32,
    System = 33,
    MaxNpadType = 34,
};

// This is nn::hid::NpadStyleSet
enum class NpadStyleSet : u32 {
    None = 0,
    Fullkey = 1U << 0,
    Handheld = 1U << 1,
    JoyDual = 1U << 2,
    JoyLeft = 1U << 3,
    JoyRight = 1U << 4,
    Gc = 1U << 5,
    Palma = 1U << 6,
    Lark = 1U << 7,
    HandheldLark = 1U << 8,
    Lucia = 1U << 9,
    Lagoon = 1U << 10,
    Lager = 1U << 11,
    SystemExt = 1U << 29,
    System = 1U << 30,

    All = 0xFFFFFFFFU,
};
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
DECLARE_ENUM_FLAG_OPERATORS(NpadStyleSet)

// This is nn::hid::VibrationDevicePosition
enum class VibrationDevicePosition : u32 {
    None = 0,
    Left = 1,
    Right = 2,
};

// This is nn::hid::VibrationDeviceType
enum class VibrationDeviceType : u32 {
    Unknown = 0,
    LinearResonantActuator = 1,
    GcErm = 2,
    N64 = 3,
};

// This is nn::hid::VibrationGcErmCommand
enum class VibrationGcErmCommand : u64 {
    Stop = 0,
    Start = 1,
    StopHard = 2,
};

// This is nn::hid::GyroscopeZeroDriftMode
enum class GyroscopeZeroDriftMode : u32 {
    Loose = 0,
    Standard = 1,
    Tight = 2,
};

// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
    UseSystemSetting,
    Finger,
    Heat2,
};

// This is nn::hid::system::NpadBatteryLevel
enum class NpadBatteryLevel : u32 {
    Empty,
    Critical,
    Low,
    High,
    Full,
};

// This is nn::hid::NpadStyleTag
struct NpadStyleTag {
    union {
        NpadStyleSet raw{};

        BitField<0, 1, u32> fullkey;
        BitField<1, 1, u32> handheld;
        BitField<2, 1, u32> joycon_dual;
        BitField<3, 1, u32> joycon_left;
        BitField<4, 1, u32> joycon_right;
        BitField<5, 1, u32> gamecube;
        BitField<6, 1, u32> palma;
        BitField<7, 1, u32> lark;
        BitField<8, 1, u32> handheld_lark;
        BitField<9, 1, u32> lucia;
        BitField<10, 1, u32> lagoon;
        BitField<11, 1, u32> lager;
        BitField<29, 1, u32> system_ext;
        BitField<30, 1, u32> system;
    };
};
static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size");

// This is nn::hid::TouchAttribute
struct TouchAttribute {
    union {
        u32 raw{};
        BitField<0, 1, u32> start_touch;
        BitField<1, 1, u32> end_touch;
    };
};
static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");

struct TouchFinger {
    u64 last_touch{};
    Common::Point<float> position{};
    u32 id{};
    TouchAttribute attribute{};
    bool pressed{};
};

// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
    TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
    INSERT_PADDING_BYTES(0xF);
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10,
              "TouchScreenConfigurationForNx is an invalid size");

struct NpadColor {
    u8 r{};
    u8 g{};
    u8 b{};
    u8 a{};
};
static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");

// This is nn::hid::NpadControllerColor
struct NpadControllerColor {
    NpadColor body{};
    NpadColor button{};
};
static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");

// This is nn::hid::AnalogStickState
struct AnalogStickState {
    s32 x{};
    s32 y{};
};
static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");

// This is nn::hid::server::NpadGcTriggerState
struct NpadGcTriggerState {
    s64 sampling_number{};
    s32 left{};
    s32 right{};
};
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");

// This is nn::hid::system::NpadPowerInfo
struct NpadPowerInfo {
    bool is_powered{};
    bool is_charging{};
    INSERT_PADDING_BYTES(0x6);
    NpadBatteryLevel battery_level{NpadBatteryLevel::Full};
};
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");

struct LedPattern {
    LedPattern() {
        raw = 0;
    }
    LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
        position1.Assign(light1);
        position2.Assign(light2);
        position3.Assign(light3);
        position4.Assign(light4);
    }
    union {
        u64 raw{};
        BitField<0, 1, u64> position1;
        BitField<1, 1, u64> position2;
        BitField<2, 1, u64> position3;
        BitField<3, 1, u64> position4;
    };
};

struct SleepButtonState {
    union {
        u64 raw{};

        // Buttons
        BitField<0, 1, u64> sleep;
    };
};
static_assert(sizeof(SleepButtonState) == 0x8, "SleepButtonState has incorrect size.");

struct HomeButtonState {
    union {
        u64 raw{};

        // Buttons
        BitField<0, 1, u64> home;
    };
};
static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size.");

struct CaptureButtonState {
    union {
        u64 raw{};

        // Buttons
        BitField<0, 1, u64> capture;
    };
};
static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size.");

struct NpadButtonState {
    union {
        NpadButton raw{};

        // Buttons
        BitField<0, 1, u64> a;
        BitField<1, 1, u64> b;
        BitField<2, 1, u64> x;
        BitField<3, 1, u64> y;
        BitField<4, 1, u64> stick_l;
        BitField<5, 1, u64> stick_r;
        BitField<6, 1, u64> l;
        BitField<7, 1, u64> r;
        BitField<8, 1, u64> zl;
        BitField<9, 1, u64> zr;
        BitField<10, 1, u64> plus;
        BitField<11, 1, u64> minus;

        // D-Pad
        BitField<12, 1, u64> left;
        BitField<13, 1, u64> up;
        BitField<14, 1, u64> right;
        BitField<15, 1, u64> down;

        // Left JoyStick
        BitField<16, 1, u64> stick_l_left;
        BitField<17, 1, u64> stick_l_up;
        BitField<18, 1, u64> stick_l_right;
        BitField<19, 1, u64> stick_l_down;

        // Right JoyStick
        BitField<20, 1, u64> stick_r_left;
        BitField<21, 1, u64> stick_r_up;
        BitField<22, 1, u64> stick_r_right;
        BitField<23, 1, u64> stick_r_down;

        BitField<24, 1, u64> left_sl;
        BitField<25, 1, u64> left_sr;

        BitField<26, 1, u64> right_sl;
        BitField<27, 1, u64> right_sr;

        BitField<28, 1, u64> palma;
        BitField<29, 1, u64> verification;
        BitField<30, 1, u64> handheld_left_b;
        BitField<31, 1, u64> lagon_c_left;
        BitField<32, 1, u64> lagon_c_up;
        BitField<33, 1, u64> lagon_c_right;
        BitField<34, 1, u64> lagon_c_down;
    };
};
static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size.");

// This is nn::hid::DebugPadButton
struct DebugPadButton {
    union {
        u32 raw{};
        BitField<0, 1, u32> a;
        BitField<1, 1, u32> b;
        BitField<2, 1, u32> x;
        BitField<3, 1, u32> y;
        BitField<4, 1, u32> l;
        BitField<5, 1, u32> r;
        BitField<6, 1, u32> zl;
        BitField<7, 1, u32> zr;
        BitField<8, 1, u32> plus;
        BitField<9, 1, u32> minus;
        BitField<10, 1, u32> d_left;
        BitField<11, 1, u32> d_up;
        BitField<12, 1, u32> d_right;
        BitField<13, 1, u32> d_down;
    };
};
static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size");

// This is nn::hid::ConsoleSixAxisSensorHandle
struct ConsoleSixAxisSensorHandle {
    u8 unknown_1{};
    u8 unknown_2{};
    INSERT_PADDING_BYTES_NOINIT(2);
};
static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
              "ConsoleSixAxisSensorHandle is an invalid size");

// This is nn::hid::SixAxisSensorHandle
struct SixAxisSensorHandle {
    NpadStyleIndex npad_type{NpadStyleIndex::None};
    u8 npad_id{};
    DeviceIndex device_index{DeviceIndex::None};
    INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");

// These parameters seem related to how much gyro/accelerometer is used
struct SixAxisSensorFusionParameters {
    f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
    f32 parameter2{0.4f};  // Default 0.4
};
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
              "SixAxisSensorFusionParameters is an invalid size");

// This is nn::hid::server::SixAxisSensorProperties
struct SixAxisSensorProperties {
    union {
        u8 raw{};
        BitField<0, 1, u8> is_newly_assigned;
        BitField<1, 1, u8> is_firmware_update_available;
    };
};
static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");

// This is nn::hid::SixAxisSensorCalibrationParameter
struct SixAxisSensorCalibrationParameter {
    std::array<u8, 0x744> unknown_data;
};
static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
              "SixAxisSensorCalibrationParameter is an invalid size");
static_assert(std::is_trivial_v<SixAxisSensorCalibrationParameter>,
              "SixAxisSensorCalibrationParameter must be trivial.");

// This is nn::hid::SixAxisSensorIcInformation
struct SixAxisSensorIcInformation {
    f32 angular_rate;                      // dps
    std::array<f32, 6> unknown_gyro_data1; // dps
    std::array<f32, 9> unknown_gyro_data2;
    std::array<f32, 9> unknown_gyro_data3;
    f32 acceleration_range;                 // g force
    std::array<f32, 6> unknown_accel_data1; // g force
    std::array<f32, 9> unknown_accel_data2;
    std::array<f32, 9> unknown_accel_data3;
};
static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
              "SixAxisSensorIcInformation is an invalid size");
static_assert(std::is_trivial_v<SixAxisSensorIcInformation>,
              "SixAxisSensorIcInformation must be trivial.");

// This is nn::hid::SixAxisSensorAttribute
struct SixAxisSensorAttribute {
    union {
        u32 raw{};
        BitField<0, 1, u32> is_connected;
        BitField<1, 1, u32> is_interpolated;
    };
};
static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");

// This is nn::hid::SixAxisSensorState
struct SixAxisSensorState {
    s64 delta_time{};
    s64 sampling_number{};
    Common::Vec3f accel{};
    Common::Vec3f gyro{};
    Common::Vec3f rotation{};
    std::array<Common::Vec3f, 3> orientation{};
    SixAxisSensorAttribute attribute{};
    INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");

// This is nn::hid::VibrationDeviceHandle
struct VibrationDeviceHandle {
    NpadStyleIndex npad_type{NpadStyleIndex::None};
    u8 npad_id{};
    DeviceIndex device_index{DeviceIndex::None};
    INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");

// This is nn::hid::VibrationValue
struct VibrationValue {
    f32 low_amplitude{};
    f32 low_frequency{};
    f32 high_amplitude{};
    f32 high_frequency{};
    bool operator==(const VibrationValue& b) {
        if (low_amplitude != b.low_amplitude || high_amplitude != b.high_amplitude) {
            return false;
        }
        // Changes in frequency without amplitude don't have any effect
        if (low_amplitude == 0 && high_amplitude == 0) {
            return true;
        }
        if (low_frequency != b.low_frequency || high_frequency != b.high_frequency) {
            return false;
        }
        return true;
    }
};
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");

constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
    .low_amplitude = 0.0f,
    .low_frequency = 160.0f,
    .high_amplitude = 0.0f,
    .high_frequency = 320.0f,
};

// This is nn::hid::VibrationDeviceInfo
struct VibrationDeviceInfo {
    VibrationDeviceType type{};
    VibrationDevicePosition position{};
};
static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");

// This is nn::hid::KeyboardModifier
struct KeyboardModifier {
    union {
        u32 raw{};
        BitField<0, 1, u32> control;
        BitField<1, 1, u32> shift;
        BitField<2, 1, u32> left_alt;
        BitField<3, 1, u32> right_alt;
        BitField<4, 1, u32> gui;
        BitField<8, 1, u32> caps_lock;
        BitField<9, 1, u32> scroll_lock;
        BitField<10, 1, u32> num_lock;
        BitField<11, 1, u32> katakana;
        BitField<12, 1, u32> hiragana;
    };
};

static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size");

// This is nn::hid::KeyboardAttribute
struct KeyboardAttribute {
    union {
        u32 raw{};
        BitField<0, 1, u32> is_connected;
    };
};
static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size");

// This is nn::hid::KeyboardKey
struct KeyboardKey {
    // This should be a 256 bit flag
    std::array<u8, 32> key{};
};
static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");

// This is nn::hid::MouseButton
struct MouseButton {
    union {
        u32_le raw{};
        BitField<0, 1, u32> left;
        BitField<1, 1, u32> right;
        BitField<2, 1, u32> middle;
        BitField<3, 1, u32> forward;
        BitField<4, 1, u32> back;
    };
};
static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size");

// This is nn::hid::MouseAttribute
struct MouseAttribute {
    union {
        u32 raw{};
        BitField<0, 1, u32> transferable;
        BitField<1, 1, u32> is_connected;
    };
};
static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size");

// This is nn::hid::detail::MouseState
struct MouseState {
    s64 sampling_number{};
    s32 x{};
    s32 y{};
    s32 delta_x{};
    s32 delta_y{};
    // Axis Order in HW is switched for the wheel
    s32 delta_wheel_y{};
    s32 delta_wheel_x{};
    MouseButton button{};
    MouseAttribute attribute{};
};
static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");

struct UniquePadId {
    u64 id;
};
static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");

// This is nn::hid::system::FirmwareVersion
struct FirmwareVersion {
    u8 major;
    u8 minor;
    u8 micro;
    u8 revision;
    std::array<char, 0xc> device_identifier;
};
static_assert(sizeof(FirmwareVersion) == 0x10, "FirmwareVersion is an invalid size");

} // namespace Core::HID


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

#pragma once

#include "hid_core/hid_result.h"
#include "hid_core/hid_types.h"

namespace Service::HID {

constexpr bool IsNpadIdValid(const Core::HID::NpadIdType npad_id) {
    switch (npad_id) {
    case Core::HID::NpadIdType::Player1:
    case Core::HID::NpadIdType::Player2:
    case Core::HID::NpadIdType::Player3:
    case Core::HID::NpadIdType::Player4:
    case Core::HID::NpadIdType::Player5:
    case Core::HID::NpadIdType::Player6:
    case Core::HID::NpadIdType::Player7:
    case Core::HID::NpadIdType::Player8:
    case Core::HID::NpadIdType::Other:
    case Core::HID::NpadIdType::Handheld:
        return true;
    default:
        return false;
    }
}

constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& handle) {
    const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id));
    const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;

    if (!npad_id) {
        return ResultInvalidNpadId;
    }
    if (!device_index) {
        return NpadDeviceIndexOutOfRange;
    }

    return ResultSuccess;
}

constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) {
    switch (handle.npad_type) {
    case Core::HID::NpadStyleIndex::Fullkey:
    case Core::HID::NpadStyleIndex::Handheld:
    case Core::HID::NpadStyleIndex::JoyconDual:
    case Core::HID::NpadStyleIndex::JoyconLeft:
    case Core::HID::NpadStyleIndex::JoyconRight:
    case Core::HID::NpadStyleIndex::GameCube:
    case Core::HID::NpadStyleIndex::N64:
    case Core::HID::NpadStyleIndex::SystemExt:
    case Core::HID::NpadStyleIndex::System:
        // These support vibration
        break;
    default:
        return ResultVibrationInvalidStyleIndex;
    }

    if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) {
        return ResultVibrationInvalidNpadId;
    }

    if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) {
        return ResultVibrationDeviceIndexOutOfRange;
    }

    return ResultSuccess;
}

/// Converts a Core::HID::NpadIdType to an array index.
constexpr size_t NpadIdTypeToIndex(Core::HID::NpadIdType npad_id_type) {
    switch (npad_id_type) {
    case Core::HID::NpadIdType::Player1:
        return 0;
    case Core::HID::NpadIdType::Player2:
        return 1;
    case Core::HID::NpadIdType::Player3:
        return 2;
    case Core::HID::NpadIdType::Player4:
        return 3;
    case Core::HID::NpadIdType::Player5:
        return 4;
    case Core::HID::NpadIdType::Player6:
        return 5;
    case Core::HID::NpadIdType::Player7:
        return 6;
    case Core::HID::NpadIdType::Player8:
        return 7;
    case Core::HID::NpadIdType::Handheld:
        return 8;
    case Core::HID::NpadIdType::Other:
        return 9;
    default:
        return 8;
    }
}

/// Converts an array index to a Core::HID::NpadIdType
constexpr Core::HID::NpadIdType IndexToNpadIdType(size_t index) {
    switch (index) {
    case 0:
        return Core::HID::NpadIdType::Player1;
    case 1:
        return Core::HID::NpadIdType::Player2;
    case 2:
        return Core::HID::NpadIdType::Player3;
    case 3:
        return Core::HID::NpadIdType::Player4;
    case 4:
        return Core::HID::NpadIdType::Player5;
    case 5:
        return Core::HID::NpadIdType::Player6;
    case 6:
        return Core::HID::NpadIdType::Player7;
    case 7:
        return Core::HID::NpadIdType::Player8;
    case 8:
        return Core::HID::NpadIdType::Handheld;
    case 9:
        return Core::HID::NpadIdType::Other;
    default:
        return Core::HID::NpadIdType::Invalid;
    }
}

constexpr Core::HID::NpadStyleSet GetStylesetByIndex(std::size_t index) {
    switch (index) {
    case 0:
        return Core::HID::NpadStyleSet::Fullkey;
    case 1:
        return Core::HID::NpadStyleSet::Handheld;
    case 2:
        return Core::HID::NpadStyleSet::JoyDual;
    case 3:
        return Core::HID::NpadStyleSet::JoyLeft;
    case 4:
        return Core::HID::NpadStyleSet::JoyRight;
    case 5:
        return Core::HID::NpadStyleSet::Palma;
    default:
        return Core::HID::NpadStyleSet::None;
    }
}

} // namespace Service::HID


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

#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "hid_core/hid_core.h"
#include "hid_core/hidbus/hidbus_base.h"

namespace Service::HID {

HidbusBase::HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
    : system(system_), service_context(service_context_) {
    send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
}

HidbusBase::~HidbusBase() {
    service_context.CloseEvent(send_command_async_event);
};

void HidbusBase::ActivateDevice() {
    if (is_activated) {
        return;
    }
    is_activated = true;
    OnInit();
}

void HidbusBase::DeactivateDevice() {
    if (is_activated) {
        OnRelease();
    }
    is_activated = false;
}

bool HidbusBase::IsDeviceActivated() const {
    return is_activated;
}

void HidbusBase::Enable(bool enable) {
    device_enabled = enable;
}

bool HidbusBase::IsEnabled() const {
    return device_enabled;
}

bool HidbusBase::IsPollingMode() const {
    return polling_mode_enabled;
}

JoyPollingMode HidbusBase::GetPollingMode() const {
    return polling_mode;
}

void HidbusBase::SetPollingMode(JoyPollingMode mode) {
    polling_mode = mode;
    polling_mode_enabled = true;
}

void HidbusBase::DisablePollingMode() {
    polling_mode_enabled = false;
}

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

Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
    return send_command_async_event->GetReadableEvent();
}

} // namespace Service::HID