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

add_library(input_common STATIC
    drivers/camera.cpp
    drivers/camera.h
    drivers/keyboard.cpp
    drivers/keyboard.h
    drivers/mouse.cpp
    drivers/mouse.h
    drivers/tas_input.cpp
    drivers/tas_input.h
    drivers/touch_screen.cpp
    drivers/touch_screen.h
    drivers/udp_client.cpp
    drivers/udp_client.h
    drivers/virtual_amiibo.cpp
    drivers/virtual_amiibo.h
    drivers/virtual_gamepad.cpp
    drivers/virtual_gamepad.h
    helpers/stick_from_buttons.cpp
    helpers/stick_from_buttons.h
    helpers/touch_from_buttons.cpp
    helpers/touch_from_buttons.h
    helpers/udp_protocol.cpp
    helpers/udp_protocol.h
    input_engine.cpp
    input_engine.h
    input_mapping.cpp
    input_mapping.h
    input_poller.cpp
    input_poller.h
    main.cpp
    main.h
    precompiled_headers.h
)

if (MSVC)
    target_compile_options(input_common PRIVATE
        /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
        /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
        /we4800 # Implicit conversion from 'type' to bool. Possible information loss
    )
else()
    target_compile_options(input_common PRIVATE
        -Werror=conversion
    )
endif()

if (ENABLE_SDL2)
    target_sources(input_common PRIVATE
        drivers/joycon.cpp
        drivers/joycon.h
        drivers/sdl_driver.cpp
        drivers/sdl_driver.h
        helpers/joycon_driver.cpp
        helpers/joycon_driver.h
        helpers/joycon_protocol/calibration.cpp
        helpers/joycon_protocol/calibration.h
        helpers/joycon_protocol/common_protocol.cpp
        helpers/joycon_protocol/common_protocol.h
        helpers/joycon_protocol/generic_functions.cpp
        helpers/joycon_protocol/generic_functions.h
        helpers/joycon_protocol/joycon_types.h
        helpers/joycon_protocol/irs.cpp
        helpers/joycon_protocol/irs.h
        helpers/joycon_protocol/nfc.cpp
        helpers/joycon_protocol/nfc.h
        helpers/joycon_protocol/poller.cpp
        helpers/joycon_protocol/poller.h
        helpers/joycon_protocol/ringcon.cpp
        helpers/joycon_protocol/ringcon.h
        helpers/joycon_protocol/rumble.cpp
        helpers/joycon_protocol/rumble.h
    )
    target_link_libraries(input_common PRIVATE SDL2::SDL2)
    target_compile_definitions(input_common PRIVATE HAVE_SDL2)
endif()

if (ENABLE_LIBUSB)
    target_sources(input_common PRIVATE
        drivers/gc_adapter.cpp
        drivers/gc_adapter.h
    )
    target_link_libraries(input_common PRIVATE libusb::usb)
    target_compile_definitions(input_common PRIVATE HAVE_LIBUSB)
endif()

create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC hid_core PRIVATE common Boost::headers)

if (YUZU_USE_PRECOMPILED_HEADERS)
    target_precompile_headers(input_common PRIVATE precompiled_headers.h)
endif()

if (ANDROID)
    target_sources(input_common PRIVATE
        drivers/android.cpp
        drivers/android.h
    )
    target_link_libraries(input_common PRIVATE android)
endif()


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

#include <set>
#include <common/settings_input.h>
#include <common/thread.h>
#include <jni.h>
#include "common/android/android_common.h"
#include "common/android/id_cache.h"
#include "input_common/drivers/android.h"

namespace InputCommon {

Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
    vibration_thread = std::jthread([this](std::stop_token token) {
        Common::SetCurrentThreadName("Android_Vibration");
        auto env = Common::Android::GetEnvForThread();
        using namespace std::chrono_literals;
        while (!token.stop_requested()) {
            SendVibrations(env, token);
        }
    });
}

Android::~Android() = default;

void Android::RegisterController(jobject j_input_device) {
    auto env = Common::Android::GetEnvForThread();
    const std::string guid = Common::Android::GetJString(
        env, static_cast<jstring>(
                 env->CallObjectMethod(j_input_device, Common::Android::GetYuzuDeviceGetGUID())));
    const s32 port = env->CallIntMethod(j_input_device, Common::Android::GetYuzuDeviceGetPort());
    const auto identifier = GetIdentifier(guid, static_cast<size_t>(port));
    PreSetController(identifier);

    if (input_devices.find(identifier) != input_devices.end()) {
        env->DeleteGlobalRef(input_devices[identifier]);
    }
    auto new_device = env->NewGlobalRef(j_input_device);
    input_devices[identifier] = new_device;
}

void Android::SetButtonState(std::string guid, size_t port, int button_id, bool value) {
    const auto identifier = GetIdentifier(guid, port);
    SetButton(identifier, button_id, value);
}

void Android::SetAxisPosition(std::string guid, size_t port, int axis_id, float value) {
    const auto identifier = GetIdentifier(guid, port);
    SetAxis(identifier, axis_id, value);
}

void Android::SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x,
                             float gyro_y, float gyro_z, float accel_x, float accel_y,
                             float accel_z) {
    const auto identifier = GetIdentifier(guid, port);
    const BasicMotion motion_data{
        .gyro_x = gyro_x,
        .gyro_y = gyro_y,
        .gyro_z = gyro_z,
        .accel_x = accel_x,
        .accel_y = accel_y,
        .accel_z = accel_z,
        .delta_timestamp = delta_timestamp,
    };
    SetMotion(identifier, 0, motion_data);
}

Common::Input::DriverResult Android::SetVibration(
    [[maybe_unused]] const PadIdentifier& identifier,
    [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
    vibration_queue.Push(VibrationRequest{
        .identifier = identifier,
        .vibration = vibration,
    });
    return Common::Input::DriverResult::Success;
}

bool Android::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
    auto device = input_devices.find(identifier);
    if (device != input_devices.end()) {
        return Common::Android::RunJNIOnFiber<bool>([&](JNIEnv* env) {
            return static_cast<bool>(env->CallBooleanMethod(
                device->second, Common::Android::GetYuzuDeviceGetSupportsVibration()));
        });
    }
    return false;
}

std::vector<Common::ParamPackage> Android::GetInputDevices() const {
    std::vector<Common::ParamPackage> devices;
    auto env = Common::Android::GetEnvForThread();
    for (const auto& [key, value] : input_devices) {
        auto name_object = static_cast<jstring>(
            env->CallObjectMethod(value, Common::Android::GetYuzuDeviceGetName()));
        const std::string name =
            fmt::format("{} {}", Common::Android::GetJString(env, name_object), key.port);
        devices.emplace_back(Common::ParamPackage{
            {"engine", GetEngineName()},
            {"display", std::move(name)},
            {"guid", key.guid.RawString()},
            {"port", std::to_string(key.port)},
        });
    }
    return devices;
}

std::set<s32> Android::GetDeviceAxes(JNIEnv* env, jobject& j_device) const {
    auto j_axes = static_cast<jobjectArray>(
        env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceGetAxes()));
    std::set<s32> axes;
    for (int i = 0; i < env->GetArrayLength(j_axes); ++i) {
        jobject axis = env->GetObjectArrayElement(j_axes, i);
        axes.insert(env->GetIntField(axis, Common::Android::GetIntegerValueField()));
    }
    return axes;
}

Common::ParamPackage Android::BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
                                                         int axis_y) const {
    Common::ParamPackage params;
    params.Set("engine", GetEngineName());
    params.Set("port", static_cast<int>(identifier.port));
    params.Set("guid", identifier.guid.RawString());
    params.Set("axis_x", axis_x);
    params.Set("axis_y", axis_y);
    params.Set("offset_x", 0);
    params.Set("offset_y", 0);
    params.Set("invert_x", "+");

    // Invert Y-Axis by default
    params.Set("invert_y", "-");
    return params;
}

Common::ParamPackage Android::BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis,
                                                               bool invert) const {
    Common::ParamPackage params{};
    params.Set("engine", GetEngineName());
    params.Set("port", static_cast<int>(identifier.port));
    params.Set("guid", identifier.guid.RawString());
    params.Set("axis", axis);
    params.Set("threshold", "0.5");
    params.Set("invert", invert ? "-" : "+");
    return params;
}

Common::ParamPackage Android::BuildButtonParamPackageForButton(PadIdentifier identifier,
                                                               s32 button) const {
    Common::ParamPackage params{};
    params.Set("engine", GetEngineName());
    params.Set("port", static_cast<int>(identifier.port));
    params.Set("guid", identifier.guid.RawString());
    params.Set("button", button);
    return params;
}

bool Android::MatchVID(Common::UUID device, const std::vector<std::string>& vids) const {
    for (size_t i = 0; i < vids.size(); ++i) {
        auto fucker = device.RawString();
        if (fucker.find(vids[i]) != std::string::npos) {
            return true;
        }
    }
    return false;
}

AnalogMapping Android::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
    if (!params.Has("guid") || !params.Has("port")) {
        return {};
    }

    auto identifier =
        GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0)));
    auto& j_device = input_devices[identifier];
    if (j_device == nullptr) {
        return {};
    }

    auto env = Common::Android::GetEnvForThread();
    std::set<s32> axes = GetDeviceAxes(env, j_device);
    if (axes.size() == 0) {
        return {};
    }

    AnalogMapping mapping = {};
    if (axes.find(AXIS_X) != axes.end() && axes.find(AXIS_Y) != axes.end()) {
        mapping.insert_or_assign(Settings::NativeAnalog::LStick,
                                 BuildParamPackageForAnalog(identifier, AXIS_X, AXIS_Y));
    }

    if (axes.find(AXIS_RX) != axes.end() && axes.find(AXIS_RY) != axes.end()) {
        mapping.insert_or_assign(Settings::NativeAnalog::RStick,
                                 BuildParamPackageForAnalog(identifier, AXIS_RX, AXIS_RY));
    } else if (axes.find(AXIS_Z) != axes.end() && axes.find(AXIS_RZ) != axes.end()) {
        mapping.insert_or_assign(Settings::NativeAnalog::RStick,
                                 BuildParamPackageForAnalog(identifier, AXIS_Z, AXIS_RZ));
    }
    return mapping;
}

ButtonMapping Android::GetButtonMappingForDevice(const Common::ParamPackage& params) {
    if (!params.Has("guid") || !params.Has("port")) {
        return {};
    }

    auto identifier =
        GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0)));
    auto& j_device = input_devices[identifier];
    if (j_device == nullptr) {
        return {};
    }

    auto env = Common::Android::GetEnvForThread();
    jintArray j_keys = env->NewIntArray(static_cast<int>(keycode_ids.size()));
    env->SetIntArrayRegion(j_keys, 0, static_cast<int>(keycode_ids.size()), keycode_ids.data());
    auto j_has_keys_object = static_cast<jbooleanArray>(
        env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceHasKeys(), j_keys));
    jboolean isCopy = false;
    jboolean* j_has_keys = env->GetBooleanArrayElements(j_has_keys_object, &isCopy);

    std::set<s32> available_keys;
    for (size_t i = 0; i < keycode_ids.size(); ++i) {
        if (j_has_keys[i]) {
            available_keys.insert(keycode_ids[i]);
        }
    }

    // Some devices use axes instead of buttons for certain controls so we need all the axes here
    std::set<s32> axes = GetDeviceAxes(env, j_device);

    ButtonMapping mapping = {};
    if (axes.find(AXIS_HAT_X) != axes.end() && axes.find(AXIS_HAT_Y) != axes.end()) {
        mapping.insert_or_assign(Settings::NativeButton::DUp,
                                 BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, true));
        mapping.insert_or_assign(Settings::NativeButton::DDown,
                                 BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, false));
        mapping.insert_or_assign(Settings::NativeButton::DLeft,
                                 BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, true));
        mapping.insert_or_assign(Settings::NativeButton::DRight,
                                 BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, false));
    } else if (available_keys.find(KEYCODE_DPAD_UP) != available_keys.end() &&
               available_keys.find(KEYCODE_DPAD_DOWN) != available_keys.end() &&
               available_keys.find(KEYCODE_DPAD_LEFT) != available_keys.end() &&
               available_keys.find(KEYCODE_DPAD_RIGHT) != available_keys.end()) {
        mapping.insert_or_assign(Settings::NativeButton::DUp,
                                 BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_UP));
        mapping.insert_or_assign(Settings::NativeButton::DDown,
                                 BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_DOWN));
        mapping.insert_or_assign(Settings::NativeButton::DLeft,
                                 BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_LEFT));
        mapping.insert_or_assign(Settings::NativeButton::DRight,
                                 BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_RIGHT));
    }

    if (axes.find(AXIS_LTRIGGER) != axes.end()) {
        mapping.insert_or_assign(Settings::NativeButton::ZL, BuildAnalogParamPackageForButton(
                                                                 identifier, AXIS_LTRIGGER, false));
    } else if (available_keys.find(KEYCODE_BUTTON_L2) != available_keys.end()) {
        mapping.insert_or_assign(Settings::NativeButton::ZL,
                                 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L2));
    }

    if (axes.find(AXIS_RTRIGGER) != axes.end()) {
        mapping.insert_or_assign(Settings::NativeButton::ZR, BuildAnalogParamPackageForButton(
                                                                 identifier, AXIS_RTRIGGER, false));
    } else if (available_keys.find(KEYCODE_BUTTON_R2) != available_keys.end()) {
        mapping.insert_or_assign(Settings::NativeButton::ZR,
                                 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R2));
    }

    if (available_keys.find(KEYCODE_BUTTON_A) != available_keys.end()) {
        if (MatchVID(identifier.guid, flipped_ab_vids)) {
            mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton(
                                                                    identifier, KEYCODE_BUTTON_A));
        } else {
            mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton(
                                                                    identifier, KEYCODE_BUTTON_A));
        }
    }
    if (available_keys.find(KEYCODE_BUTTON_B) != available_keys.end()) {
        if (MatchVID(identifier.guid, flipped_ab_vids)) {
            mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton(
                                                                    identifier, KEYCODE_BUTTON_B));
        } else {
            mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton(
                                                                    identifier, KEYCODE_BUTTON_B));
        }
    }
    if (available_keys.find(KEYCODE_BUTTON_X) != available_keys.end()) {
        if (MatchVID(identifier.guid, flipped_xy_vids)) {
            mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton(
                                                                    identifier, KEYCODE_BUTTON_X));
        } else {
            mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton(
                                                                    identifier, KEYCODE_BUTTON_X));
        }
    }
    if (available_keys.find(KEYCODE_BUTTON_Y) != available_keys.end()) {
        if (MatchVID(identifier.guid, flipped_xy_vids)) {
            mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton(
                                                                    identifier, KEYCODE_BUTTON_Y));
        } else {
            mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton(
                                                                    identifier, KEYCODE_BUTTON_Y));
        }
    }

    if (available_keys.find(KEYCODE_BUTTON_L1) != available_keys.end()) {
        mapping.insert_or_assign(Settings::NativeButton::L,
                                 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L1));
    }
    if (available_keys.find(KEYCODE_BUTTON_R1) != available_keys.end()) {
        mapping.insert_or_assign(Settings::NativeButton::R,
                                 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R1));
    }

    if (available_keys.find(KEYCODE_BUTTON_THUMBL) != available_keys.end()) {
        mapping.insert_or_assign(
            Settings::NativeButton::LStick,
            BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBL));
    }
    if (available_keys.find(KEYCODE_BUTTON_THUMBR) != available_keys.end()) {
        mapping.insert_or_assign(
            Settings::NativeButton::RStick,
            BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBR));
    }

    if (available_keys.find(KEYCODE_BUTTON_START) != available_keys.end()) {
        mapping.insert_or_assign(
            Settings::NativeButton::Plus,
            BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_START));
    }
    if (available_keys.find(KEYCODE_BUTTON_SELECT) != available_keys.end()) {
        mapping.insert_or_assign(
            Settings::NativeButton::Minus,
            BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_SELECT));
    }

    return mapping;
}

Common::Input::ButtonNames Android::GetUIName(
    [[maybe_unused]] const Common::ParamPackage& params) const {
    return Common::Input::ButtonNames::Value;
}

PadIdentifier Android::GetIdentifier(const std::string& guid, size_t port) const {
    return {
        .guid = Common::UUID{guid},
        .port = port,
        .pad = 0,
    };
}

void Android::SendVibrations(JNIEnv* env, std::stop_token token) {
    VibrationRequest request = vibration_queue.PopWait(token);
    auto device = input_devices.find(request.identifier);
    if (device != input_devices.end()) {
        float average_intensity = static_cast<float>(
            (request.vibration.high_amplitude + request.vibration.low_amplitude) / 2.0);
        env->CallVoidMethod(device->second, Common::Android::GetYuzuDeviceVibrate(),
                            average_intensity);
    }
}

} // namespace InputCommon

repo_name: litucks/torzu

license: mit


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

#pragma once

#include <set>
#include <common/threadsafe_queue.h>
#include <jni.h>
#include "input_common/input_engine.h"

namespace InputCommon {

/**
 * A virtual controller that is always assigned to the game input
 */
class Android final : public InputEngine {
public:
    explicit Android(std::string input_engine_);

    ~Android() override;

    /**
     * Registers controller number to accept new inputs.
     * @param j_input_device YuzuInputDevice object from the Android frontend to register.
     */
    void RegisterController(jobject j_input_device);

    /**
     * Sets the status of a button on a specific controller.
     * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
     * @param port Port determined by controller connection order.
     * @param button_id The Android Keycode corresponding to this event.
     * @param value Whether the button is pressed or not.
     */
    void SetButtonState(std::string guid, size_t port, int button_id, bool value);

    /**
     * Sets the status of an axis on a specific controller.
     * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
     * @param port Port determined by controller connection order.
     * @param axis_id The Android axis ID corresponding to this event.
     * @param value Value along the given axis.
     */
    void SetAxisPosition(std::string guid, size_t port, int axis_id, float value);

    /**
     * Sets the status of the motion sensor on a specific controller
     * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
     * @param port Port determined by controller connection order.
     * @param delta_timestamp Time passed since the last read.
     * @param gyro_x,gyro_y,gyro_z Gyro sensor readings.
     * @param accel_x,accel_y,accel_z Accelerometer sensor readings.
     */
    void SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x,
                        float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z);

    Common::Input::DriverResult SetVibration(
        const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;

    bool IsVibrationEnabled(const PadIdentifier& identifier) override;

    std::vector<Common::ParamPackage> GetInputDevices() const override;

    /**
     * Gets the axes reported by the YuzuInputDevice.
     * @param env JNI environment pointer.
     * @param j_device YuzuInputDevice from the Android frontend.
     * @return Set of the axes reported by the underlying Android InputDevice
     */
    std::set<s32> GetDeviceAxes(JNIEnv* env, jobject& j_device) const;

    Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
                                                    int axis_y) const;

    Common::ParamPackage BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis,
                                                          bool invert) const;

    Common::ParamPackage BuildButtonParamPackageForButton(PadIdentifier identifier,
                                                          s32 button) const;

    bool MatchVID(Common::UUID device, const std::vector<std::string>& vids) const;

    AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;

    ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;

    Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;

private:
    std::unordered_map<PadIdentifier, jobject> input_devices;

    /// Returns the correct identifier corresponding to the player index
    PadIdentifier GetIdentifier(const std::string& guid, size_t port) const;

    /// Takes all vibrations from the queue and sends the command to the controller
    void SendVibrations(JNIEnv* env, std::stop_token token);

    static constexpr s32 AXIS_X = 0;
    static constexpr s32 AXIS_Y = 1;
    static constexpr s32 AXIS_Z = 11;
    static constexpr s32 AXIS_RX = 12;
    static constexpr s32 AXIS_RY = 13;
    static constexpr s32 AXIS_RZ = 14;
    static constexpr s32 AXIS_HAT_X = 15;
    static constexpr s32 AXIS_HAT_Y = 16;
    static constexpr s32 AXIS_LTRIGGER = 17;
    static constexpr s32 AXIS_RTRIGGER = 18;

    static constexpr s32 KEYCODE_DPAD_UP = 19;
    static constexpr s32 KEYCODE_DPAD_DOWN = 20;
    static constexpr s32 KEYCODE_DPAD_LEFT = 21;
    static constexpr s32 KEYCODE_DPAD_RIGHT = 22;
    static constexpr s32 KEYCODE_BUTTON_A = 96;
    static constexpr s32 KEYCODE_BUTTON_B = 97;
    static constexpr s32 KEYCODE_BUTTON_X = 99;
    static constexpr s32 KEYCODE_BUTTON_Y = 100;
    static constexpr s32 KEYCODE_BUTTON_L1 = 102;
    static constexpr s32 KEYCODE_BUTTON_R1 = 103;
    static constexpr s32 KEYCODE_BUTTON_L2 = 104;
    static constexpr s32 KEYCODE_BUTTON_R2 = 105;
    static constexpr s32 KEYCODE_BUTTON_THUMBL = 106;
    static constexpr s32 KEYCODE_BUTTON_THUMBR = 107;
    static constexpr s32 KEYCODE_BUTTON_START = 108;
    static constexpr s32 KEYCODE_BUTTON_SELECT = 109;
    const std::vector<s32> keycode_ids{
        KEYCODE_DPAD_UP,       KEYCODE_DPAD_DOWN,     KEYCODE_DPAD_LEFT,    KEYCODE_DPAD_RIGHT,
        KEYCODE_BUTTON_A,      KEYCODE_BUTTON_B,      KEYCODE_BUTTON_X,     KEYCODE_BUTTON_Y,
        KEYCODE_BUTTON_L1,     KEYCODE_BUTTON_R1,     KEYCODE_BUTTON_L2,    KEYCODE_BUTTON_R2,
        KEYCODE_BUTTON_THUMBL, KEYCODE_BUTTON_THUMBR, KEYCODE_BUTTON_START, KEYCODE_BUTTON_SELECT,
    };

    const std::string sony_vid{"054c"};
    const std::string nintendo_vid{"057e"};
    const std::string razer_vid{"1532"};
    const std::string redmagic_vid{"3537"};
    const std::string backbone_labs_vid{"358a"};
    const std::string xbox_vid{"045e"};
    const std::vector<std::string> flipped_ab_vids{sony_vid,     nintendo_vid,      razer_vid,
                                                   redmagic_vid, backbone_labs_vid, xbox_vid};
    const std::vector<std::string> flipped_xy_vids{sony_vid, razer_vid, redmagic_vid,
                                                   backbone_labs_vid, xbox_vid};

    /// Queue of vibration request to controllers
    Common::SPSCQueue<VibrationRequest> vibration_queue;
    std::jthread vibration_thread;
};

} // namespace InputCommon


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

#include <fmt/format.h>

#include "common/param_package.h"
#include "input_common/drivers/camera.h"

namespace InputCommon {
constexpr PadIdentifier identifier = {
    .guid = Common::UUID{},
    .port = 0,
    .pad = 0,
};

Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
    PreSetController(identifier);
}

void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) {
    const std::size_t desired_width = getImageWidth();
    const std::size_t desired_height = getImageHeight();
    status.data.resize(desired_width * desired_height);

    // Resize image to desired format
    for (std::size_t y = 0; y < desired_height; y++) {
        for (std::size_t x = 0; x < desired_width; x++) {
            const std::size_t pixel_index = y * desired_width + x;
            const std::size_t old_x = width * x / desired_width;
            const std::size_t old_y = height * y / desired_height;
            const std::size_t data_pixel_index = old_y * width + old_x;
            status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF);
        }
    }

    SetCamera(identifier, status);
}

std::size_t Camera::getImageWidth() const {
    switch (status.format) {
    case Common::Input::CameraFormat::Size320x240:
        return 320;
    case Common::Input::CameraFormat::Size160x120:
        return 160;
    case Common::Input::CameraFormat::Size80x60:
        return 80;
    case Common::Input::CameraFormat::Size40x30:
        return 40;
    case Common::Input::CameraFormat::Size20x15:
        return 20;
    case Common::Input::CameraFormat::None:
    default:
        return 0;
    }
}

std::size_t Camera::getImageHeight() const {
    switch (status.format) {
    case Common::Input::CameraFormat::Size320x240:
        return 240;
    case Common::Input::CameraFormat::Size160x120:
        return 120;
    case Common::Input::CameraFormat::Size80x60:
        return 60;
    case Common::Input::CameraFormat::Size40x30:
        return 30;
    case Common::Input::CameraFormat::Size20x15:
        return 15;
    case Common::Input::CameraFormat::None:
    default:
        return 0;
    }
}

Common::Input::DriverResult Camera::SetCameraFormat(
    [[maybe_unused]] const PadIdentifier& identifier_,
    const Common::Input::CameraFormat camera_format) {
    status.format = camera_format;
    return Common::Input::DriverResult::Success;
}

} // namespace InputCommon


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

#pragma once

#include <span>

#include "input_common/input_engine.h"

namespace InputCommon {

/**
 * A button device factory representing a keyboard. It receives keyboard events and forward them
 * to all button devices it created.
 */
class Camera final : public InputEngine {
public:
    explicit Camera(std::string input_engine_);

    void SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data);

    std::size_t getImageWidth() const;
    std::size_t getImageHeight() const;

    Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_,
                                                Common::Input::CameraFormat camera_format) override;

private:
    Common::Input::CameraStatus status{};
};

} // namespace InputCommon


code:
// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

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

#include "common/logging/log.h"
#include "common/param_package.h"
#include "common/polyfill_thread.h"
#include "common/settings_input.h"
#include "common/thread.h"
#include "input_common/drivers/gc_adapter.h"

namespace InputCommon {

class LibUSBContext {
public:
    explicit LibUSBContext() {
        init_result = libusb_init(&ctx);
    }

    ~LibUSBContext() {
        libusb_exit(ctx);
    }

    LibUSBContext& operator=(const LibUSBContext&) = delete;
    LibUSBContext(const LibUSBContext&) = delete;

    LibUSBContext& operator=(LibUSBContext&&) noexcept = delete;
    LibUSBContext(LibUSBContext&&) noexcept = delete;

    [[nodiscard]] int InitResult() const noexcept {
        return init_result;
    }

    [[nodiscard]] libusb_context* get() noexcept {
        return ctx;
    }

private:
    libusb_context* ctx;
    int init_result{};
};

class LibUSBDeviceHandle {
public:
    explicit LibUSBDeviceHandle(libusb_context* ctx, uint16_t vid, uint16_t pid) noexcept {
        handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
    }

    ~LibUSBDeviceHandle() noexcept {
        if (handle) {
            libusb_release_interface(handle, 1);
            libusb_close(handle);
        }
    }

    LibUSBDeviceHandle& operator=(const LibUSBDeviceHandle&) = delete;
    LibUSBDeviceHandle(const LibUSBDeviceHandle&) = delete;

    LibUSBDeviceHandle& operator=(LibUSBDeviceHandle&&) noexcept = delete;
    LibUSBDeviceHandle(LibUSBDeviceHandle&&) noexcept = delete;

    [[nodiscard]] libusb_device_handle* get() noexcept {
        return handle;
    }

private:
    libusb_device_handle* handle{};
};

GCAdapter::GCAdapter(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
    if (usb_adapter_handle) {
        return;
    }
    LOG_DEBUG(Input, "Initialization started");

    libusb_ctx = std::make_unique<LibUSBContext>();
    const int init_res = libusb_ctx->InitResult();
    if (init_res == LIBUSB_SUCCESS) {
        adapter_scan_thread =
            std::jthread([this](std::stop_token stop_token) { AdapterScanThread(stop_token); });
    } else {
        LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
    }
}

GCAdapter::~GCAdapter() {
    Reset();
}

void GCAdapter::AdapterInputThread(std::stop_token stop_token) {
    LOG_DEBUG(Input, "Input thread started");
    Common::SetCurrentThreadName("GCAdapter");
    s32 payload_size{};
    AdapterPayload adapter_payload{};

    adapter_scan_thread = {};

    while (!stop_token.stop_requested()) {
        libusb_interrupt_transfer(usb_adapter_handle->get(), input_endpoint, adapter_payload.data(),
                                  static_cast<s32>(adapter_payload.size()), &payload_size, 16);
        if (IsPayloadCorrect(adapter_payload, payload_size)) {
            UpdateControllers(adapter_payload);
            UpdateVibrations();
        }
        std::this_thread::yield();
    }

    if (restart_scan_thread) {
        adapter_scan_thread =
            std::jthread([this](std::stop_token token) { AdapterScanThread(token); });
        restart_scan_thread = false;
    }
}

bool GCAdapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
    if (payload_size != static_cast<s32>(adapter_payload.size()) ||
        adapter_payload[0] != LIBUSB_DT_HID) {
        LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
                  adapter_payload[0]);
        if (input_error_counter++ > 20) {
            LOG_ERROR(Input, "Timeout, Is the adapter connected?");
            adapter_input_thread.request_stop();
            restart_scan_thread = true;
        }
        return false;
    }

    input_error_counter = 0;
    return true;
}

void GCAdapter::UpdateControllers(const AdapterPayload& adapter_payload) {
    for (std::size_t port = 0; port < pads.size(); ++port) {
        const std::size_t offset = 1 + (9 * port);
        const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
        UpdatePadType(port, type);
        if (DeviceConnected(port)) {
            const u8 b1 = adapter_payload[offset + 1];
            const u8 b2 = adapter_payload[offset + 2];
            UpdateStateButtons(port, b1, b2);
            UpdateStateAxes(port, adapter_payload);
        }
    }
}

void GCAdapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
    if (pads[port].type == pad_type) {
        return;
    }
    // Device changed reset device and set new type
    pads[port].axis_origin = {};
    pads[port].reset_origin_counter = {};
    pads[port].enable_vibration = {};
    pads[port].rumble_amplitude = {};
    pads[port].type = pad_type;
}

void GCAdapter::UpdateStateButtons(std::size_t port, [[maybe_unused]] u8 b1,
                                   [[maybe_unused]] u8 b2) {
    if (port >= pads.size()) {
        return;
    }

    static constexpr std::array<PadButton, 8> b1_buttons{
        PadButton::ButtonA,    PadButton::ButtonB,     PadButton::ButtonX,    PadButton::ButtonY,
        PadButton::ButtonLeft, PadButton::ButtonRight, PadButton::ButtonDown, PadButton::ButtonUp,
    };

    static constexpr std::array<PadButton, 4> b2_buttons{
        PadButton::ButtonStart,
        PadButton::TriggerZ,
        PadButton::TriggerR,
        PadButton::TriggerL,
    };

    for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
        const bool button_status = (b1 & (1U << i)) != 0;
        const int button = static_cast<int>(b1_buttons[i]);
        SetButton(pads[port].identifier, button, button_status);
    }

    for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
        const bool button_status = (b2 & (1U << j)) != 0;
        const int button = static_cast<int>(b2_buttons[j]);
        SetButton(pads[port].identifier, button, button_status);
    }
}

void GCAdapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
    if (port >= pads.size()) {
        return;
    }

    const std::size_t offset = 1 + (9 * port);
    static constexpr std::array<PadAxes, 6> axes{
        PadAxes::StickX,    PadAxes::StickY,      PadAxes::SubstickX,
        PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
    };

    for (const PadAxes axis : axes) {
        const auto index = static_cast<std::size_t>(axis);
        const u8 axis_value = adapter_payload[offset + 3 + index];
        if (pads[port].reset_origin_counter <= 18) {
            if (pads[port].axis_origin[index] != axis_value) {
                pads[port].reset_origin_counter = 0;
            }
            pads[port].axis_origin[index] = axis_value;
            pads[port].reset_origin_counter++;
        }
        const f32 axis_status = (axis_value - pads[port].axis_origin[index]) / 100.0f;
        SetAxis(pads[port].identifier, static_cast<int>(index), axis_status);
    }
}

void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
    Common::SetCurrentThreadName("ScanGCAdapter");
    usb_adapter_handle = nullptr;
    pads = {};
    while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
    }
}

bool GCAdapter::Setup() {
    constexpr u16 nintendo_vid = 0x057e;
    constexpr u16 gc_adapter_pid = 0x0337;
    usb_adapter_handle =
        std::make_unique<LibUSBDeviceHandle>(libusb_ctx->get(), nintendo_vid, gc_adapter_pid);
    if (!usb_adapter_handle->get()) {
        return false;
    }
    if (!CheckDeviceAccess()) {
        usb_adapter_handle = nullptr;
        return false;
    }

    libusb_device* const device = libusb_get_device(usb_adapter_handle->get());

    LOG_INFO(Input, "GC adapter is now connected");
    // GC Adapter found and accessible, registering it
    if (GetGCEndpoint(device)) {
        rumble_enabled = true;
        input_error_counter = 0;
        output_error_counter = 0;

        std::size_t port = 0;
        for (GCController& pad : pads) {
            pad.identifier = {
                .guid = Common::UUID{},
                .port = port++,
                .pad = 0,
            };
            PreSetController(pad.identifier);
        }

        adapter_input_thread =
            std::jthread([this](std::stop_token stop_token) { AdapterInputThread(stop_token); });
        return true;
    }
    return false;
}

bool GCAdapter::CheckDeviceAccess() {
    s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle->get(), 0);
    if (kernel_driver_error == 1) {
        kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle->get(), 0);
        if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
            LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
                      kernel_driver_error);
        }
    }

    if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
        usb_adapter_handle = nullptr;
        return false;
    }

    const int interface_claim_error = libusb_claim_interface(usb_adapter_handle->get(), 0);
    if (interface_claim_error) {
        LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
        usb_adapter_handle = nullptr;
        return false;
    }

    // This fixes payload problems from offbrand GCAdapters
    const s32 control_transfer_error =
        libusb_control_transfer(usb_adapter_handle->get(), 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
    if (control_transfer_error < 0) {
        LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
    }

    return true;
}

bool GCAdapter::GetGCEndpoint(libusb_device* device) {
    libusb_config_descriptor* config = nullptr;
    const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
    if (config_descriptor_return != LIBUSB_SUCCESS) {
        LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
                  config_descriptor_return);
        return false;
    }

    for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
        const libusb_interface* interfaceContainer = &config->interface[ic];
        for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
            const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i];
            for (u8 e = 0; e < interface->bNumEndpoints; e++) {
                const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
                if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) != 0) {
                    input_endpoint = endpoint->bEndpointAddress;
                } else {
                    output_endpoint = endpoint->bEndpointAddress;
                }
            }
        }
    }
    // This transfer seems to be responsible for clearing the state of the adapter
    // Used to clear the "busy" state of when the device is unexpectedly unplugged
    unsigned char clear_payload = 0x13;
    libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, &clear_payload,
                              sizeof(clear_payload), nullptr, 16);
    return true;
}

Common::Input::DriverResult GCAdapter::SetVibration(
    const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
    const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
    const auto processed_amplitude =
        static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);

    pads[identifier.port].rumble_amplitude = processed_amplitude;

    if (!rumble_enabled) {
        return Common::Input::DriverResult::Disabled;
    }
    return Common::Input::DriverResult::Success;
}

bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
    return rumble_enabled;
}

void GCAdapter::UpdateVibrations() {
    // Use 8 states to keep the switching between on/off fast enough for
    // a human to feel different vibration strength
    // More states == more rumble strengths == slower update time
    constexpr u8 vibration_states = 8;

    vibration_counter = (vibration_counter + 1) % vibration_states;

    for (GCController& pad : pads) {
        const bool vibrate = pad.rumble_amplitude > vibration_counter;
        vibration_changed |= vibrate != pad.enable_vibration;
        pad.enable_vibration = vibrate;
    }
    SendVibrations();
}

void GCAdapter::SendVibrations() {
    if (!rumble_enabled || !vibration_changed) {
        return;
    }
    s32 size{};
    constexpr u8 rumble_command = 0x11;
    const u8 p1 = pads[0].enable_vibration;
    const u8 p2 = pads[1].enable_vibration;
    const u8 p3 = pads[2].enable_vibration;
    const u8 p4 = pads[3].enable_vibration;
    std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
    const int err =
        libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, payload.data(),
                                  static_cast<s32>(payload.size()), &size, 16);
    if (err) {
        LOG_DEBUG(Input, "Libusb write failed: {}", libusb_error_name(err));
        if (output_error_counter++ > 5) {
            LOG_ERROR(Input, "Output timeout, Rumble disabled");
            rumble_enabled = false;
        }
        return;
    }
    output_error_counter = 0;
    vibration_changed = false;
}

bool GCAdapter::DeviceConnected(std::size_t port) const {
    return pads[port].type != ControllerTypes::None;
}

void GCAdapter::Reset() {
    adapter_scan_thread = {};
    adapter_input_thread = {};
    usb_adapter_handle = nullptr;
    pads = {};
    libusb_ctx = nullptr;
}

std::vector<Common::ParamPackage> GCAdapter::GetInputDevices() const {
    std::vector<Common::ParamPackage> devices;
    for (std::size_t port = 0; port < pads.size(); ++port) {
        if (!DeviceConnected(port)) {
            continue;
        }
        Common::ParamPackage identifier{};
        identifier.Set("engine", GetEngineName());
        identifier.Set("display", fmt::format("Gamecube Controller {}", port + 1));
        identifier.Set("port", static_cast<int>(port));
        devices.emplace_back(identifier);
    }
    return devices;
}

ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& params) {
    // This list is missing ZL/ZR since those are not considered buttons.
    // We will add those afterwards
    // This list also excludes any button that can't be really mapped
    static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 14>
        switch_to_gcadapter_button = {
            std::pair{Settings::NativeButton::A, PadButton::ButtonA},
            {Settings::NativeButton::B, PadButton::ButtonB},
            {Settings::NativeButton::X, PadButton::ButtonX},
            {Settings::NativeButton::Y, PadButton::ButtonY},
            {Settings::NativeButton::Plus, PadButton::ButtonStart},
            {Settings::NativeButton::DLeft, PadButton::ButtonLeft},
            {Settings::NativeButton::DUp, PadButton::ButtonUp},
            {Settings::NativeButton::DRight, PadButton::ButtonRight},
            {Settings::NativeButton::DDown, PadButton::ButtonDown},
            {Settings::NativeButton::SLLeft, PadButton::TriggerL},
            {Settings::NativeButton::SRLeft, PadButton::TriggerR},
            {Settings::NativeButton::SLRight, PadButton::TriggerL},
            {Settings::NativeButton::SRRight, PadButton::TriggerR},
            {Settings::NativeButton::R, PadButton::TriggerZ},
        };
    if (!params.Has("port")) {
        return {};
    }

    ButtonMapping mapping{};
    for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) {
        Common::ParamPackage button_params{};
        button_params.Set("engine", GetEngineName());
        button_params.Set("port", params.Get("port", 0));
        button_params.Set("button", static_cast<int>(gcadapter_button));
        mapping.insert_or_assign(switch_button, std::move(button_params));
    }

    // Add the missing bindings for ZL/ZR
    static constexpr std::array<std::tuple<Settings::NativeButton::Values, PadButton, PadAxes>, 2>
        switch_to_gcadapter_axis = {
            std::tuple{Settings::NativeButton::ZL, PadButton::TriggerL, PadAxes::TriggerLeft},
            {Settings::NativeButton::ZR, PadButton::TriggerR, PadAxes::TriggerRight},
        };
    for (const auto& [switch_button, gcadapter_button, gcadapter_axis] : switch_to_gcadapter_axis) {
        Common::ParamPackage button_params{};
        button_params.Set("engine", GetEngineName());
        button_params.Set("port", params.Get("port", 0));
        button_params.Set("button", static_cast<s32>(gcadapter_button));
        button_params.Set("axis", static_cast<s32>(gcadapter_axis));
        button_params.Set("threshold", 0.5f);
        button_params.Set("range", 1.9f);
        button_params.Set("direction", "+");
        mapping.insert_or_assign(switch_button, std::move(button_params));
    }
    return mapping;
}

AnalogMapping GCAdapter::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
    if (!params.Has("port")) {
        return {};
    }

    AnalogMapping mapping = {};
    Common::ParamPackage left_analog_params;
    left_analog_params.Set("engine", GetEngineName());
    left_analog_params.Set("port", params.Get("port", 0));
    left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX));
    left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY));
    mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
    Common::ParamPackage right_analog_params;
    right_analog_params.Set("engine", GetEngineName());
    right_analog_params.Set("port", params.Get("port", 0));
    right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX));
    right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY));
    mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
    return mapping;
}

Common::Input::ButtonNames GCAdapter::GetUIButtonName(const Common::ParamPackage& params) const {
    PadButton button = static_cast<PadButton>(params.Get("button", 0));
    switch (button) {
    case PadButton::ButtonLeft:
        return Common::Input::ButtonNames::ButtonLeft;
    case PadButton::ButtonRight:
        return Common::Input::ButtonNames::ButtonRight;
    case PadButton::ButtonDown:
        return Common::Input::ButtonNames::ButtonDown;
    case PadButton::ButtonUp:
        return Common::Input::ButtonNames::ButtonUp;
    case PadButton::TriggerZ:
        return Common::Input::ButtonNames::TriggerZ;
    case PadButton::TriggerR:
        return Common::Input::ButtonNames::TriggerR;
    case PadButton::TriggerL:
        return Common::Input::ButtonNames::TriggerL;
    case PadButton::ButtonA:
        return Common::Input::ButtonNames::ButtonA;
    case PadButton::ButtonB:
        return Common::Input::ButtonNames::ButtonB;
    case PadButton::ButtonX:
        return Common::Input::ButtonNames::ButtonX;
    case PadButton::ButtonY:
        return Common::Input::ButtonNames::ButtonY;
    case PadButton::ButtonStart:
        return Common::Input::ButtonNames::ButtonStart;
    default:
        return Common::Input::ButtonNames::Undefined;
    }
}

Common::Input::ButtonNames GCAdapter::GetUIName(const Common::ParamPackage& params) const {
    if (params.Has("button")) {
        return GetUIButtonName(params);
    }
    if (params.Has("axis")) {
        return Common::Input::ButtonNames::Value;
    }

    return Common::Input::ButtonNames::Invalid;
}

bool GCAdapter::IsStickInverted(const Common::ParamPackage& params) {
    if (!params.Has("port")) {
        return false;
    }

    const auto x_axis = static_cast<PadAxes>(params.Get("axis_x", 0));
    const auto y_axis = static_cast<PadAxes>(params.Get("axis_y", 0));
    if (x_axis != PadAxes::StickY && x_axis != PadAxes::SubstickY) {
        return false;
    }
    if (y_axis != PadAxes::StickX && y_axis != PadAxes::SubstickX) {
        return false;
    }
    return true;
}

} // namespace InputCommon


code:
// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <memory>
#include <string>
#include <thread>

#include "common/polyfill_thread.h"
#include "input_common/input_engine.h"

struct libusb_context;
struct libusb_device;
struct libusb_device_handle;

namespace InputCommon {

class LibUSBContext;
class LibUSBDeviceHandle;

class GCAdapter : public InputEngine {
public:
    explicit GCAdapter(std::string input_engine_);
    ~GCAdapter() override;

    Common::Input::DriverResult SetVibration(
        const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;

    bool IsVibrationEnabled(const PadIdentifier& identifier) override;

    /// Used for automapping features
    std::vector<Common::ParamPackage> GetInputDevices() const override;
    ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
    AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
    Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;

    bool IsStickInverted(const Common::ParamPackage& params) override;

private:
    enum class PadButton {
        Undefined = 0x0000,
        ButtonLeft = 0x0001,
        ButtonRight = 0x0002,
        ButtonDown = 0x0004,
        ButtonUp = 0x0008,
        TriggerZ = 0x0010,
        TriggerR = 0x0020,
        TriggerL = 0x0040,
        ButtonA = 0x0100,
        ButtonB = 0x0200,
        ButtonX = 0x0400,
        ButtonY = 0x0800,
        ButtonStart = 0x1000,
    };

    enum class PadAxes : u8 {
        StickX,
        StickY,
        SubstickX,
        SubstickY,
        TriggerLeft,
        TriggerRight,
        Undefined,
    };

    enum class ControllerTypes {
        None,
        Wired,
        Wireless,
    };

    struct GCController {
        ControllerTypes type = ControllerTypes::None;
        PadIdentifier identifier{};
        bool enable_vibration = false;
        u8 rumble_amplitude{};
        std::array<u8, 6> axis_origin{};
        u8 reset_origin_counter{};
    };

    using AdapterPayload = std::array<u8, 37>;

    void UpdatePadType(std::size_t port, ControllerTypes pad_type);
    void UpdateControllers(const AdapterPayload& adapter_payload);
    void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
    void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);

    void AdapterInputThread(std::stop_token stop_token);

    void AdapterScanThread(std::stop_token stop_token);

    bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);

    /// For use in initialization, querying devices to find the adapter
    bool Setup();

    /// Returns true if we successfully gain access to GC Adapter
    bool CheckDeviceAccess();

    /// Captures GC Adapter endpoint address
    /// Returns true if the endpoint was set correctly
    bool GetGCEndpoint(libusb_device* device);

    /// Returns true if there is a device connected to port
    bool DeviceConnected(std::size_t port) const;

    /// For shutting down, clear all data, join all threads, release usb
    void Reset();

    void UpdateVibrations();

    /// Updates vibration state of all controllers
    void SendVibrations();

    Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;

    std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle;
    std::array<GCController, 4> pads;

    std::jthread adapter_input_thread;
    std::jthread adapter_scan_thread;
    bool restart_scan_thread{};

    std::unique_ptr<LibUSBContext> libusb_ctx;

    u8 input_endpoint{0};
    u8 output_endpoint{0};
    u8 input_error_counter{0};
    u8 output_error_counter{0};
    int vibration_counter{0};

    bool rumble_enabled{true};
    bool vibration_changed{true};
};
} // namespace InputCommon


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

#include <fmt/format.h>

#include "common/param_package.h"
#include "common/polyfill_ranges.h"
#include "common/polyfill_thread.h"
#include "common/settings.h"
#include "common/thread.h"
#include "input_common/drivers/joycon.h"
#include "input_common/helpers/joycon_driver.h"
#include "input_common/helpers/joycon_protocol/joycon_types.h"

namespace InputCommon {

Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
    // Avoid conflicting with SDL driver
    if (!Settings::values.enable_joycon_driver && !Settings::values.enable_procon_driver) {
        return;
    }
    LOG_INFO(Input, "Joycon driver Initialization started");
    const int init_res = SDL_hid_init();
    if (init_res == 0) {
        Setup();
    } else {
        LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res);
    }
}

Joycons::~Joycons() {
    Reset();
}

void Joycons::Reset() {
    scan_thread = {};
    for (const auto& device : left_joycons) {
        if (!device) {
            continue;
        }
        device->Stop();
    }
    for (const auto& device : right_joycons) {
        if (!device) {
            continue;
        }
        device->Stop();
    }
    for (const auto& device : pro_controller) {
        if (!device) {
            continue;
        }
        device->Stop();
    }
    SDL_hid_exit();
}

void Joycons::Setup() {
    u32 port = 0;
    PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
    for (auto& device : left_joycons) {
        PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
        device = std::make_shared<Joycon::JoyconDriver>(port++);
    }
    port = 0;
    for (auto& device : right_joycons) {
        PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
        device = std::make_shared<Joycon::JoyconDriver>(port++);
    }
    port = 0;
    for (auto& device : pro_controller) {
        PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro));
        device = std::make_shared<Joycon::JoyconDriver>(port++);
    }

    scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
}

void Joycons::ScanThread(std::stop_token stop_token) {
    constexpr u16 nintendo_vendor_id = 0x057e;
    Common::SetCurrentThreadName("JoyconScanThread");

    do {
        SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
        SDL_hid_device_info* cur_dev = devs;

        while (cur_dev) {
            if (IsDeviceNew(cur_dev)) {
                LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
                          cur_dev->product_id);
                RegisterNewDevice(cur_dev);
            }
            cur_dev = cur_dev->next;
        }

        SDL_hid_free_enumeration(devs);
    } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
}

bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
    Joycon::ControllerType type{};
    Joycon::SerialNumber serial_number{};

    const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
    if (result != Common::Input::DriverResult::Success) {
        return false;
    }

    const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
    if (result2 != Common::Input::DriverResult::Success) {
        return false;
    }

    auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
        if (!device) {
            return false;
        }
        if (!device->IsConnected()) {
            return false;
        }
        if (device->GetHandleSerialNumber() != serial_number) {
            return false;
        }
        return true;
    };

    // Check if device already exist
    switch (type) {
    case Joycon::ControllerType::Left:
        if (!Settings::values.enable_joycon_driver) {
            return false;
        }
        for (const auto& device : left_joycons) {
            if (is_handle_identical(device)) {
                return false;
            }
        }
        break;
    case Joycon::ControllerType::Right:
        if (!Settings::values.enable_joycon_driver) {
            return false;
        }
        for (const auto& device : right_joycons) {
            if (is_handle_identical(device)) {
                return false;
            }
        }
        break;
    case Joycon::ControllerType::Pro:
        if (!Settings::values.enable_procon_driver) {
            return false;
        }
        for (const auto& device : pro_controller) {
            if (is_handle_identical(device)) {
                return false;
            }
        }
        break;
    default:
        return false;
    }

    return true;
}

void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
    Joycon::ControllerType type{};
    auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
    auto handle = GetNextFreeHandle(type);
    if (handle == nullptr) {
        LOG_WARNING(Input, "No free handles available");
        return;
    }
    if (result == Common::Input::DriverResult::Success) {
        result = handle->RequestDeviceAccess(device_info);
    }
    if (result == Common::Input::DriverResult::Success) {
        LOG_WARNING(Input, "Initialize device");

        const std::size_t port = handle->GetDevicePort();
        const Joycon::JoyconCallbacks callbacks{
            .on_battery_data = {[this, port, type](Joycon::Battery value) {
                OnBatteryUpdate(port, type, value);
            }},
            .on_color_data = {[this, port, type](Joycon::Color value) {
                OnColorUpdate(port, type, value);
            }},
            .on_button_data = {[this, port, type](int id, bool value) {
                OnButtonUpdate(port, type, id, value);
            }},
            .on_stick_data = {[this, port, type](int id, f32 value) {
                OnStickUpdate(port, type, id, value);
            }},
            .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
                OnMotionUpdate(port, type, id, value);
            }},
            .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
            .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) {
                OnAmiiboUpdate(port, type, tag_info);
            }},
            .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
                                            Joycon::IrsResolution format) {
                OnCameraUpdate(port, camera_data, format);
            }},
        };

        handle->InitializeDevice();
        handle->SetCallbacks(callbacks);
    }
}

std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
    Joycon::ControllerType type) const {
    if (type == Joycon::ControllerType::Left) {
        const auto unconnected_device =
            std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
        if (unconnected_device != left_joycons.end()) {
            return *unconnected_device;
        }
    }
    if (type == Joycon::ControllerType::Right) {
        const auto unconnected_device = std::ranges::find_if(
            right_joycons, [](auto& device) { return !device->IsConnected(); });

        if (unconnected_device != right_joycons.end()) {
            return *unconnected_device;
        }
    }
    if (type == Joycon::ControllerType::Pro) {
        const auto unconnected_device = std::ranges::find_if(
            pro_controller, [](auto& device) { return !device->IsConnected(); });

        if (unconnected_device != pro_controller.end()) {
            return *unconnected_device;
        }
    }
    return nullptr;
}

bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
    const auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return false;
    }
    return handle->IsVibrationEnabled();
}

Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
                                                  const Common::Input::VibrationStatus& vibration) {
    const Joycon::VibrationValue native_vibration{
        .low_amplitude = vibration.low_amplitude,
        .low_frequency = vibration.low_frequency,
        .high_amplitude = vibration.high_amplitude,
        .high_frequency = vibration.high_frequency,
    };
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::DriverResult::InvalidHandle;
    }

    handle->SetVibration(native_vibration);
    return Common::Input::DriverResult::Success;
}

Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
                                             const Common::Input::LedStatus& led_status) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::DriverResult::InvalidHandle;
    }
    int led_config = led_status.led_1 ? 1 : 0;
    led_config += led_status.led_2 ? 2 : 0;
    led_config += led_status.led_3 ? 4 : 0;
    led_config += led_status.led_4 ? 8 : 0;

    return handle->SetLedConfig(static_cast<u8>(led_config));
}

Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
                                                     Common::Input::CameraFormat camera_format) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::DriverResult::InvalidHandle;
    }
    return handle->SetIrsConfig(Joycon::IrsMode::ImageTransfer,
                                static_cast<Joycon::IrsResolution>(camera_format));
};

Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
    return Common::Input::NfcState::Success;
};

Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::NfcState::Unknown;
    }
    return TranslateDriverResult(handle->StartNfcPolling());
};

Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::NfcState::Unknown;
    }
    return TranslateDriverResult(handle->StopNfcPolling());
};

Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier,
                                                std::vector<u8>& out_data) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::NfcState::Unknown;
    }
    return TranslateDriverResult(handle->ReadAmiiboData(out_data));
}

Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
                                              const std::vector<u8>& data) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::NfcState::Unknown;
    }
    return TranslateDriverResult(handle->WriteNfcData(data));
};

Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier,
                                                const Common::Input::MifareRequest& request,
                                                Common::Input::MifareRequest& data) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::NfcState::Unknown;
    }

    const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
    std::vector<Joycon::MifareReadChunk> read_request{};
    for (const auto& request_data : request.data) {
        if (request_data.command == 0) {
            continue;
        }
        Joycon::MifareReadChunk chunk = {
            .command = command,
            .sector_key = {},
            .sector = request_data.sector,
        };
        memcpy(chunk.sector_key.data(), request_data.key.data(),
               sizeof(Joycon::MifareReadChunk::sector_key));
        read_request.emplace_back(chunk);
    }

    std::vector<Joycon::MifareReadData> read_data(read_request.size());
    const auto result = handle->ReadMifareData(read_request, read_data);
    if (result == Common::Input::DriverResult::Success) {
        for (std::size_t i = 0; i < read_request.size(); i++) {
            data.data[i] = {
                .command = static_cast<u8>(command),
                .sector = read_data[i].sector,
                .key = {},
                .data = read_data[i].data,
            };
        }
    }
    return TranslateDriverResult(result);
};

Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier,
                                                 const Common::Input::MifareRequest& request) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        return Common::Input::NfcState::Unknown;
    }

    const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
    std::vector<Joycon::MifareWriteChunk> write_request{};
    for (const auto& request_data : request.data) {
        if (request_data.command == 0) {
            continue;
        }
        Joycon::MifareWriteChunk chunk = {
            .command = command,
            .sector_key = {},
            .sector = request_data.sector,
            .data = {},
        };
        memcpy(chunk.sector_key.data(), request_data.key.data(),
               sizeof(Joycon::MifareReadChunk::sector_key));
        memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data));
        write_request.emplace_back(chunk);
    }

    return TranslateDriverResult(handle->WriteMifareData(write_request));
};

Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
                                                    const Common::Input::PollingMode polling_mode) {
    auto handle = GetHandle(identifier);
    if (handle == nullptr) {
        LOG_ERROR(Input, "Invalid handle {}", identifier.port);
        return Common::Input::DriverResult::InvalidHandle;
    }

    switch (polling_mode) {
    case Common::Input::PollingMode::Active:
        return handle->SetActiveMode();
    case Common::Input::PollingMode::Passive:
        return handle->SetPassiveMode();
    case Common::Input::PollingMode::IR:
        return handle->SetIrMode();
    case Common::Input::PollingMode::NFC:
        return handle->SetNfcMode();
    case Common::Input::PollingMode::Ring:
        return handle->SetRingConMode();
    default:
        return Common::Input::DriverResult::NotSupported;
    }
}

void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
                              Joycon::Battery value) {
    const auto identifier = GetIdentifier(port, type);
    if (value.charging != 0) {
        SetBattery(identifier, Common::Input::BatteryLevel::Charging);
        return;
    }

    Common::Input::BatteryLevel battery{};
    switch (value.status) {
    case 0:
        battery = Common::Input::BatteryLevel::Empty;
        break;
    case 1:
        battery = Common::Input::BatteryLevel::Critical;
        break;
    case 2:
        battery = Common::Input::BatteryLevel::Low;
        break;
    case 3:
        battery = Common::Input::BatteryLevel::Medium;
        break;
    case 4:
    default:
        battery = Common::Input::BatteryLevel::Full;
        break;
    }
    SetBattery(identifier, battery);
}

void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
                            const Joycon::Color& value) {
    const auto identifier = GetIdentifier(port, type);
    Common::Input::BodyColorStatus color{
        .body = value.body,
        .buttons = value.buttons,
        .left_grip = value.left_grip,
        .right_grip = value.right_grip,
    };
    SetColor(identifier, color);
}

void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
    const auto identifier = GetIdentifier(port, type);
    SetButton(identifier, id, value);
}

void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
    const auto identifier = GetIdentifier(port, type);
    SetAxis(identifier, id, value);
}

void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
                             const Joycon::MotionData& value) {
    const auto identifier = GetIdentifier(port, type);
    BasicMotion motion_data{
        .gyro_x = value.gyro_x,
        .gyro_y = value.gyro_y,
        .gyro_z = value.gyro_z,
        .accel_x = value.accel_x,
        .accel_y = value.accel_y,
        .accel_z = value.accel_z,
        .delta_timestamp = 15000,
    };
    SetMotion(identifier, id, motion_data);
}

void Joycons::OnRingConUpdate(f32 ring_data) {
    // To simplify ring detection it will always be mapped to an empty identifier for all
    // controllers
    static constexpr PadIdentifier identifier = {
        .guid = Common::UUID{},
        .port = 0,
        .pad = 0,
    };
    SetAxis(identifier, 100, ring_data);
}

void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
                             const Joycon::TagInfo& tag_info) {
    const auto identifier = GetIdentifier(port, type);
    const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved
                                                     : Common::Input::NfcState::NewAmiibo;

    const Common::Input::NfcStatus nfc_status{
        .state = nfc_state,
        .uuid_length = tag_info.uuid_length,
        .protocol = tag_info.protocol,
        .tag_type = tag_info.tag_type,
        .uuid = tag_info.uuid,
    };

    SetNfc(identifier, nfc_status);
}

void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
                             Joycon::IrsResolution format) {
    const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
    SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
}

std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
    auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
        if (!device) {
            return false;
        }
        if (!device->IsConnected()) {
            return false;
        }
        if (device->GetDevicePort() == identifier.port) {
            return true;
        }
        return false;
    };
    const auto type = static_cast<Joycon::ControllerType>(identifier.pad);

    if (type == Joycon::ControllerType::Left) {
        const auto matching_device = std::ranges::find_if(
            left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });

        if (matching_device != left_joycons.end()) {
            return *matching_device;
        }
    }

    if (type == Joycon::ControllerType::Right) {
        const auto matching_device = std::ranges::find_if(
            right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });

        if (matching_device != right_joycons.end()) {
            return *matching_device;
        }
    }

    if (type == Joycon::ControllerType::Pro) {
        const auto matching_device = std::ranges::find_if(
            pro_controller, [is_handle_active](auto& device) { return is_handle_active(device); });

        if (matching_device != pro_controller.end()) {
            return *matching_device;
        }
    }

    return nullptr;
}

PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
    const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
                                  0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
    return {
        .guid = Common::UUID{guid},
        .port = port,
        .pad = static_cast<std::size_t>(type),
    };
}

Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
    const auto identifier = GetIdentifier(port, type);
    return {
        {"engine", GetEngineName()},
        {"guid", identifier.guid.RawString()},
        {"port", std::to_string(identifier.port)},
        {"pad", std::to_string(identifier.pad)},
    };
}

std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
    std::vector<Common::ParamPackage> devices{};

    auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
        if (!device) {
            return;
        }
        if (!device->IsConnected()) {
            return;
        }
        auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
        std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
                                       device->GetDevicePort() + 1);
        param.Set("display", std::move(name));
        devices.emplace_back(param);
    };

    for (const auto& controller : left_joycons) {
        add_entry(controller);
    }
    for (const auto& controller : right_joycons) {
        add_entry(controller);
    }
    for (const auto& controller : pro_controller) {
        add_entry(controller);
    }

    // List dual joycon pairs
    for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
        if (!left_joycons[i] || !right_joycons[i]) {
            continue;
        }
        if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
            continue;
        }
        auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
        const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
        const auto type = Joycon::ControllerType::Dual;
        std::string name = fmt::format("{} {}", JoyconName(type), i + 1);

        main_param.Set("display", std::move(name));
        main_param.Set("guid2", second_param.Get("guid", ""));
        main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
        devices.emplace_back(main_param);
    }

    return devices;
}

ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
    static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
                                18>
        switch_to_joycon_button = {
            std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
            {Settings::NativeButton::B, Joycon::PadButton::B, true},
            {Settings::NativeButton::X, Joycon::PadButton::X, true},
            {Settings::NativeButton::Y, Joycon::PadButton::Y, true},
            {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
            {Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
            {Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
            {Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
            {Settings::NativeButton::L, Joycon::PadButton::L, false},
            {Settings::NativeButton::R, Joycon::PadButton::R, true},
            {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
            {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
            {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
            {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
            {Settings::NativeButton::Home, Joycon::PadButton::Home, true},
            {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
            {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
            {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
        };

    if (!params.Has("port")) {
        return {};
    }

    ButtonMapping mapping{};
    for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
        const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
        auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
        if (pad == Joycon::ControllerType::Dual) {
            pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
        }

        Common::ParamPackage button_params = GetParamPackage(port, pad);
        button_params.Set("button", static_cast<int>(joycon_button));
        mapping.insert_or_assign(switch_button, std::move(button_params));
    }

    // Map SL and SR buttons for left joycons
    if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
        const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
        Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);

        Common::ParamPackage sl_button_params = button_params;
        Common::ParamPackage sr_button_params = button_params;
        sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
        sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
        mapping.insert_or_assign(Settings::NativeButton::SLLeft, std::move(sl_button_params));
        mapping.insert_or_assign(Settings::NativeButton::SRLeft, std::move(sr_button_params));
    }

    // Map SL and SR buttons for right joycons
    if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
        const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
        Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);

        Common::ParamPackage sl_button_params = button_params;
        Common::ParamPackage sr_button_params = button_params;
        sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
        sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
        mapping.insert_or_assign(Settings::NativeButton::SLRight, std::move(sl_button_params));
        mapping.insert_or_assign(Settings::NativeButton::SRRight, std::move(sr_button_params));
    }

    return mapping;
}

AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
    if (!params.Has("port")) {
        return {};
    }

    const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
    auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
    auto pad_right = pad_left;
    if (pad_left == Joycon::ControllerType::Dual) {
        pad_left = Joycon::ControllerType::Left;
        pad_right = Joycon::ControllerType::Right;
    }

    AnalogMapping mapping = {};
    Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
    left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
    left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
    mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
    Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
    right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
    right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
    mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
    return mapping;
}

MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
    if (!params.Has("port")) {
        return {};
    }

    const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
    auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
    auto pad_right = pad_left;
    if (pad_left == Joycon::ControllerType::Dual) {
        pad_left = Joycon::ControllerType::Left;
        pad_right = Joycon::ControllerType::Right;
    }

    MotionMapping mapping = {};
    Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
    left_motion_params.Set("motion", 0);
    mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
    Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
    right_Motion_params.Set("motion", 1);
    mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
    return mapping;
}

Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
    const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
    switch (button) {
    case Joycon::PadButton::Left:
        return Common::Input::ButtonNames::ButtonLeft;
    case Joycon::PadButton::Right:
        return Common::Input::ButtonNames::ButtonRight;
    case Joycon::PadButton::Down:
        return Common::Input::ButtonNames::ButtonDown;
    case Joycon::PadButton::Up:
        return Common::Input::ButtonNames::ButtonUp;
    case Joycon::PadButton::LeftSL:
    case Joycon::PadButton::RightSL:
        return Common::Input::ButtonNames::TriggerSL;
    case Joycon::PadButton::LeftSR:
    case Joycon::PadButton::RightSR:
        return Common::Input::ButtonNames::TriggerSR;
    case Joycon::PadButton::L:
        return Common::Input::ButtonNames::TriggerL;
    case Joycon::PadButton::R:
        return Common::Input::ButtonNames::TriggerR;
    case Joycon::PadButton::ZL:
        return Common::Input::ButtonNames::TriggerZL;
    case Joycon::PadButton::ZR:
        return Common::Input::ButtonNames::TriggerZR;
    case Joycon::PadButton::A:
        return Common::Input::ButtonNames::ButtonA;
    case Joycon::PadButton::B:
        return Common::Input::ButtonNames::ButtonB;
    case Joycon::PadButton::X:
        return Common::Input::ButtonNames::ButtonX;
    case Joycon::PadButton::Y:
        return Common::Input::ButtonNames::ButtonY;
    case Joycon::PadButton::Plus:
        return Common::Input::ButtonNames::ButtonPlus;
    case Joycon::PadButton::Minus:
        return Common::Input::ButtonNames::ButtonMinus;
    case Joycon::PadButton::Home:
        return Common::Input::ButtonNames::ButtonHome;
    case Joycon::PadButton::Capture:
        return Common::Input::ButtonNames::ButtonCapture;
    case Joycon::PadButton::StickL:
        return Common::Input::ButtonNames::ButtonStickL;
    case Joycon::PadButton::StickR:
        return Common::Input::ButtonNames::ButtonStickR;
    default:
        return Common::Input::ButtonNames::Undefined;
    }
}

Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
    if (params.Has("button")) {
        return GetUIButtonName(params);
    }
    if (params.Has("axis")) {
        return Common::Input::ButtonNames::Value;
    }
    if (params.Has("motion")) {
        return Common::Input::ButtonNames::Engine;
    }

    return Common::Input::ButtonNames::Invalid;
}

std::string Joycons::JoyconName(Joycon::ControllerType type) const {
    switch (type) {
    case Joycon::ControllerType::Left:
        return "Left Joycon";
    case Joycon::ControllerType::Right:
        return "Right Joycon";
    case Joycon::ControllerType::Pro:
        return "Pro Controller";
    case Joycon::ControllerType::Dual:
        return "Dual Joycon";
    default:
        return "Unknown Switch Controller";
    }
}

Common::Input::NfcState Joycons::TranslateDriverResult(Common::Input::DriverResult result) const {
    switch (result) {
    case Common::Input::DriverResult::Success:
        return Common::Input::NfcState::Success;
    case Common::Input::DriverResult::Disabled:
        return Common::Input::NfcState::WrongDeviceState;
    case Common::Input::DriverResult::NotSupported:
        return Common::Input::NfcState::NotSupported;
    default:
        return Common::Input::NfcState::Unknown;
    }
}

} // namespace InputCommon


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

#pragma once

#include <array>
#include <span>
#include <thread>
#include <SDL_hidapi.h>

#include "input_common/input_engine.h"

namespace InputCommon::Joycon {
using SerialNumber = std::array<u8, 15>;
struct Battery;
struct Color;
struct MotionData;
struct TagInfo;
enum class ControllerType : u8;
enum class IrsResolution;
class JoyconDriver;
} // namespace InputCommon::Joycon

namespace InputCommon {

class Joycons final : public InputCommon::InputEngine {
public:
    explicit Joycons(const std::string& input_engine_);

    ~Joycons();

    bool IsVibrationEnabled(const PadIdentifier& identifier) override;
    Common::Input::DriverResult SetVibration(
        const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;

    Common::Input::DriverResult SetLeds(const PadIdentifier& identifier,
                                        const Common::Input::LedStatus& led_status) override;

    Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
                                                Common::Input::CameraFormat camera_format) override;

    Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override;
    Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override;
    Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override;
    Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier,
                                           std::vector<u8>& out_data) override;
    Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,
                                         const std::vector<u8>& data) override;
    Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier,
                                           const Common::Input::MifareRequest& request,
                                           Common::Input::MifareRequest& out_data) override;
    Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier,
                                            const Common::Input::MifareRequest& request) override;

    Common::Input::DriverResult SetPollingMode(
        const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;

    /// Used for automapping features
    std::vector<Common::ParamPackage> GetInputDevices() const override;
    ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
    AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
    MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
    Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;

private:
    static constexpr std::size_t MaxSupportedControllers = 8;

    /// For shutting down, clear all data, join all threads, release usb devices
    void Reset();

    /// Registers controllers, clears all data and starts the scan thread
    void Setup();

    /// Actively searches for new devices
    void ScanThread(std::stop_token stop_token);

    /// Returns true if device is valid and not registered
    bool IsDeviceNew(SDL_hid_device_info* device_info) const;

    /// Tries to connect to the new device
    void RegisterNewDevice(SDL_hid_device_info* device_info);

    /// Returns the next free handle
    std::shared_ptr<Joycon::JoyconDriver> GetNextFreeHandle(Joycon::ControllerType type) const;

    void OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, Joycon::Battery value);
    void OnColorUpdate(std::size_t port, Joycon::ControllerType type, const Joycon::Color& value);
    void OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value);
    void OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value);
    void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
                        const Joycon::MotionData& value);
    void OnRingConUpdate(f32 ring_data);
    void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
                        const Joycon::TagInfo& amiibo_data);
    void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
                        Joycon::IrsResolution format);

    /// Returns a JoyconHandle corresponding to a PadIdentifier
    std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const;

    /// Returns a PadIdentifier corresponding to the port number and joycon type
    PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const;

    /// Returns a ParamPackage corresponding to the port number and joycon type
    Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const;

    std::string JoyconName(std::size_t port) const;

    Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;

    /// Returns the name of the device in text format
    std::string JoyconName(Joycon::ControllerType type) const;

    Common::Input::NfcState TranslateDriverResult(Common::Input::DriverResult result) const;

    std::jthread scan_thread;

    // Joycon types are split by type to ease supporting dualjoycon configurations
    std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{};
    std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{};
    std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> pro_controller{};
};

} // namespace InputCommon


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

#include "common/param_package.h"
#include "common/settings_input.h"
#include "input_common/drivers/keyboard.h"

namespace InputCommon {

constexpr PadIdentifier key_identifier = {
    .guid = Common::UUID{},
    .port = 0,
    .pad = 0,
};
constexpr PadIdentifier keyboard_key_identifier = {
    .guid = Common::UUID{},
    .port = 1,
    .pad = 0,
};
constexpr PadIdentifier keyboard_modifier_identifier = {
    .guid = Common::UUID{},
    .port = 1,
    .pad = 1,
};

Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
    // Keyboard is broken into 3 different sets:
    // key: Unfiltered intended for controllers.
    // keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation.
    // keyboard_modifier: Allows only Settings::NativeKeyboard::Modifiers intended for keyboard
    // emulation.
    PreSetController(key_identifier);
    PreSetController(keyboard_key_identifier);
    PreSetController(keyboard_modifier_identifier);
}

void Keyboard::PressKey(int key_code) {
    SetButton(key_identifier, key_code, true);
}

void Keyboard::ReleaseKey(int key_code) {
    SetButton(key_identifier, key_code, false);
}

void Keyboard::PressKeyboardKey(int key_index) {
    if (key_index == Settings::NativeKeyboard::None) {
        return;
    }
    SetButton(keyboard_key_identifier, key_index, true);
}

void Keyboard::ReleaseKeyboardKey(int key_index) {
    if (key_index == Settings::NativeKeyboard::None) {
        return;
    }
    SetButton(keyboard_key_identifier, key_index, false);
}

void Keyboard::SetKeyboardModifiers(int key_modifiers) {
    for (int i = 0; i < 32; ++i) {
        bool key_value = ((key_modifiers >> i) & 0x1) != 0;
        SetButton(keyboard_modifier_identifier, i, key_value);
        // Use the modifier to press the key button equivalent
        switch (i) {
        case Settings::NativeKeyboard::LeftControl:
            SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftControlKey, key_value);
            break;
        case Settings::NativeKeyboard::LeftShift:
            SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftShiftKey, key_value);
            break;
        case Settings::NativeKeyboard::LeftAlt:
            SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftAltKey, key_value);
            break;
        case Settings::NativeKeyboard::LeftMeta:
            SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftMetaKey, key_value);
            break;
        case Settings::NativeKeyboard::RightControl:
            SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightControlKey,
                      key_value);
            break;
        case Settings::NativeKeyboard::RightShift:
            SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightShiftKey, key_value);
            break;
        case Settings::NativeKeyboard::RightAlt:
            SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightAltKey, key_value);
            break;
        case Settings::NativeKeyboard::RightMeta:
            SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightMetaKey, key_value);
            break;
        default:
            // Other modifier keys should be pressed with PressKey since they stay enabled until
            // next press
            break;
        }
    }
}

void Keyboard::ReleaseAllKeys() {
    ResetButtonState();
}

std::vector<Common::ParamPackage> Keyboard::GetInputDevices() const {
    std::vector<Common::ParamPackage> devices;
    devices.emplace_back(Common::ParamPackage{
        {"engine", GetEngineName()},
        {"display", "Keyboard Only"},
    });
    return devices;
}

} // namespace InputCommon


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

#pragma once

#include "input_common/input_engine.h"

namespace InputCommon {

/**
 * A button device factory representing a keyboard. It receives keyboard events and forward them
 * to all button devices it created.
 */
class Keyboard final : public InputEngine {
public:
    explicit Keyboard(std::string input_engine_);

    /**
     * Sets the status of all buttons bound with the key to pressed
     * @param key_code the code of the key to press
     */
    void PressKey(int key_code);

    /**
     * Sets the status of all buttons bound with the key to released
     * @param key_code the code of the key to release
     */
    void ReleaseKey(int key_code);

    /**
     * Sets the status of the keyboard key to pressed
     * @param key_index index of the key to press
     */
    void PressKeyboardKey(int key_index);

    /**
     * Sets the status of the keyboard key to released
     * @param key_index index of the key to release
     */
    void ReleaseKeyboardKey(int key_index);

    /**
     * Sets the status of all keyboard modifier keys
     * @param key_modifiers the code of the key to release
     */
    void SetKeyboardModifiers(int key_modifiers);

    /// Sets all keys to the non pressed state
    void ReleaseAllKeys();

    /// Used for automapping features
    std::vector<Common::ParamPackage> GetInputDevices() const override;
};

} // namespace InputCommon