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

#pragma once

#include "common/param_package.h"
#include "common/threadsafe_queue.h"

namespace InputCommon::Polling {
enum class InputType;
}

namespace InputCommon {
class InputEngine;
struct MappingData;

class MappingFactory {
public:
    MappingFactory();

    /**
     * Resets all variables to begin the mapping process
     * @param type type of input desired to be returned
     */
    void BeginMapping(Polling::InputType type);

    /// Returns an input event with mapping information from the input_queue
    [[nodiscard]] Common::ParamPackage GetNextInput();

    /**
     * Registers mapping input data from the driver
     * @param data A struct containing all the information needed to create a proper
     *             ParamPackage
     */
    void RegisterInput(const MappingData& data);

    /// Stop polling from all backends
    void StopMapping();

private:
    /**
     * If provided data satisfies the requirements it will push an element to the input_queue
     * Supported input:
     *     - Button: Creates a basic button ParamPackage
     *     - HatButton: Creates a basic hat button ParamPackage
     *     - Analog: Creates a basic analog ParamPackage
     * @param data A struct containing all the information needed to create a proper
     * ParamPackage
     */
    void RegisterButton(const MappingData& data);

    /**
     * If provided data satisfies the requirements it will push an element to the input_queue
     * Supported input:
     *     - Button, HatButton: Pass the data to RegisterButton
     *     - Analog: Stores the first axis and on the second axis creates a basic stick ParamPackage
     * @param data A struct containing all the information needed to create a proper
     *             ParamPackage
     */
    void RegisterStick(const MappingData& data);

    /**
     * If provided data satisfies the requirements it will push an element to the input_queue
     * Supported input:
     *     - Button, HatButton: Pass the data to RegisterButton
     *     - Analog: Stores the first two axis and on the third axis creates a basic Motion
     * ParamPackage
     *     - Motion: Creates a basic Motion ParamPackage
     * @param data A struct containing all the information needed to create a proper
     *             ParamPackage
     */
    void RegisterMotion(const MappingData& data);

    /**
     * Returns true if driver can be mapped
     * @param data A struct containing all the information needed to create a proper
     *             ParamPackage
     */
    bool IsDriverValid(const MappingData& data) const;

    Common::SPSCQueue<Common::ParamPackage> input_queue;
    Polling::InputType input_type{Polling::InputType::None};
    bool is_enabled{};
    int first_axis = -1;
    int second_axis = -1;
};

} // namespace InputCommon


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

#include "common/common_types.h"
#include "common/input.h"

#include "input_common/input_engine.h"
#include "input_common/input_poller.h"

namespace InputCommon {

class DummyInput final : public Common::Input::InputDevice {
public:
    explicit DummyInput() = default;
};

class InputFromButton final : public Common::Input::InputDevice {
public:
    explicit InputFromButton(PadIdentifier identifier_, int button_, bool turbo_, bool toggle_,
                             bool inverted_, InputEngine* input_engine_)
        : identifier(identifier_), button(button_), turbo(turbo_), toggle(toggle_),
          inverted(inverted_), input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Button,
            .index = button,
            .callback = engine_callback,
        };
        last_button_value = false;
        callback_key = input_engine->SetCallback(input_identifier);
    }

    ~InputFromButton() override {
        input_engine->DeleteCallback(callback_key);
    }

    Common::Input::ButtonStatus GetStatus() const {
        return {
            .value = input_engine->GetButton(identifier, button),
            .inverted = inverted,
            .toggle = toggle,
            .turbo = turbo,
        };
    }

    void ForceUpdate() override {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Button,
            .button_status = GetStatus(),
        };

        last_button_value = status.button_status.value;
        TriggerOnChange(status);
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Button,
            .button_status = GetStatus(),
        };

        if (status.button_status.value != last_button_value) {
            last_button_value = status.button_status.value;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    const int button;
    const bool turbo;
    const bool toggle;
    const bool inverted;
    int callback_key;
    bool last_button_value;
    InputEngine* input_engine;
};

class InputFromHatButton final : public Common::Input::InputDevice {
public:
    explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool turbo_,
                                bool toggle_, bool inverted_, InputEngine* input_engine_)
        : identifier(identifier_), button(button_), direction(direction_), turbo(turbo_),
          toggle(toggle_), inverted(inverted_), input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier input_identifier{
            .identifier = identifier,
            .type = EngineInputType::HatButton,
            .index = button,
            .callback = engine_callback,
        };
        last_button_value = false;
        callback_key = input_engine->SetCallback(input_identifier);
    }

    ~InputFromHatButton() override {
        input_engine->DeleteCallback(callback_key);
    }

    Common::Input::ButtonStatus GetStatus() const {
        return {
            .value = input_engine->GetHatButton(identifier, button, direction),
            .inverted = inverted,
            .toggle = toggle,
            .turbo = turbo,
        };
    }

    void ForceUpdate() override {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Button,
            .button_status = GetStatus(),
        };

        last_button_value = status.button_status.value;
        TriggerOnChange(status);
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Button,
            .button_status = GetStatus(),
        };

        if (status.button_status.value != last_button_value) {
            last_button_value = status.button_status.value;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    const int button;
    const u8 direction;
    const bool turbo;
    const bool toggle;
    const bool inverted;
    int callback_key;
    bool last_button_value;
    InputEngine* input_engine;
};

class InputFromStick final : public Common::Input::InputDevice {
public:
    explicit InputFromStick(PadIdentifier identifier_, int axis_x_, int axis_y_,
                            Common::Input::AnalogProperties properties_x_,
                            Common::Input::AnalogProperties properties_y_,
                            InputEngine* input_engine_)
        : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
          properties_y(properties_y_),
          input_engine(input_engine_), invert_axis_y{input_engine_->GetEngineName() == "sdl"} {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier x_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis_x,
            .callback = engine_callback,
        };
        const InputIdentifier y_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis_y,
            .callback = engine_callback,
        };
        last_axis_x_value = 0.0f;
        last_axis_y_value = 0.0f;
        callback_key_x = input_engine->SetCallback(x_input_identifier);
        callback_key_y = input_engine->SetCallback(y_input_identifier);
    }

    ~InputFromStick() override {
        input_engine->DeleteCallback(callback_key_x);
        input_engine->DeleteCallback(callback_key_y);
    }

    Common::Input::StickStatus GetStatus() const {
        Common::Input::StickStatus status;
        status.x = {
            .raw_value = input_engine->GetAxis(identifier, axis_x),
            .properties = properties_x,
        };
        status.y = {
            .raw_value = input_engine->GetAxis(identifier, axis_y),
            .properties = properties_y,
        };
        // This is a workaround to keep compatibility with old yuzu versions. Vertical axis is
        // inverted on SDL compared to Nintendo
        if (invert_axis_y) {
            status.y.raw_value = -status.y.raw_value;
        }
        return status;
    }

    void ForceUpdate() override {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Stick,
            .stick_status = GetStatus(),
        };

        last_axis_x_value = status.stick_status.x.raw_value;
        last_axis_y_value = status.stick_status.y.raw_value;
        TriggerOnChange(status);
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Stick,
            .stick_status = GetStatus(),
        };

        if (status.stick_status.x.raw_value != last_axis_x_value ||
            status.stick_status.y.raw_value != last_axis_y_value) {
            last_axis_x_value = status.stick_status.x.raw_value;
            last_axis_y_value = status.stick_status.y.raw_value;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    const int axis_x;
    const int axis_y;
    const Common::Input::AnalogProperties properties_x;
    const Common::Input::AnalogProperties properties_y;
    int callback_key_x;
    int callback_key_y;
    float last_axis_x_value;
    float last_axis_y_value;
    InputEngine* input_engine;
    const bool invert_axis_y;
};

class InputFromTouch final : public Common::Input::InputDevice {
public:
    explicit InputFromTouch(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
                            int axis_x_, int axis_y_, Common::Input::AnalogProperties properties_x_,
                            Common::Input::AnalogProperties properties_y_,
                            InputEngine* input_engine_)
        : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
          axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
          properties_y(properties_y_), input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier button_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Button,
            .index = button,
            .callback = engine_callback,
        };
        const InputIdentifier x_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis_x,
            .callback = engine_callback,
        };
        const InputIdentifier y_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis_y,
            .callback = engine_callback,
        };
        last_axis_x_value = 0.0f;
        last_axis_y_value = 0.0f;
        last_button_value = false;
        callback_key_button = input_engine->SetCallback(button_input_identifier);
        callback_key_x = input_engine->SetCallback(x_input_identifier);
        callback_key_y = input_engine->SetCallback(y_input_identifier);
    }

    ~InputFromTouch() override {
        input_engine->DeleteCallback(callback_key_button);
        input_engine->DeleteCallback(callback_key_x);
        input_engine->DeleteCallback(callback_key_y);
    }

    Common::Input::TouchStatus GetStatus() const {
        Common::Input::TouchStatus status{};
        status.pressed = {
            .value = input_engine->GetButton(identifier, button),
            .inverted = inverted,
            .toggle = toggle,
        };
        status.x = {
            .raw_value = input_engine->GetAxis(identifier, axis_x),
            .properties = properties_x,
        };
        status.y = {
            .raw_value = input_engine->GetAxis(identifier, axis_y),
            .properties = properties_y,
        };
        return status;
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Touch,
            .touch_status = GetStatus(),
        };

        if (status.touch_status.x.raw_value != last_axis_x_value ||
            status.touch_status.y.raw_value != last_axis_y_value ||
            status.touch_status.pressed.value != last_button_value) {
            last_axis_x_value = status.touch_status.x.raw_value;
            last_axis_y_value = status.touch_status.y.raw_value;
            last_button_value = status.touch_status.pressed.value;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    const int button;
    const bool toggle;
    const bool inverted;
    const int axis_x;
    const int axis_y;
    const Common::Input::AnalogProperties properties_x;
    const Common::Input::AnalogProperties properties_y;
    int callback_key_button;
    int callback_key_x;
    int callback_key_y;
    bool last_button_value;
    float last_axis_x_value;
    float last_axis_y_value;
    InputEngine* input_engine;
};

class InputFromTrigger final : public Common::Input::InputDevice {
public:
    explicit InputFromTrigger(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
                              int axis_, Common::Input::AnalogProperties properties_,
                              InputEngine* input_engine_)
        : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
          axis(axis_), properties(properties_), input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier button_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Button,
            .index = button,
            .callback = engine_callback,
        };
        const InputIdentifier axis_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis,
            .callback = engine_callback,
        };
        last_axis_value = 0.0f;
        last_button_value = false;
        callback_key_button = input_engine->SetCallback(button_input_identifier);
        axis_callback_key = input_engine->SetCallback(axis_input_identifier);
    }

    ~InputFromTrigger() override {
        input_engine->DeleteCallback(callback_key_button);
        input_engine->DeleteCallback(axis_callback_key);
    }

    Common::Input::TriggerStatus GetStatus() const {
        const Common::Input::AnalogStatus analog_status{
            .raw_value = input_engine->GetAxis(identifier, axis),
            .properties = properties,
        };
        const Common::Input::ButtonStatus button_status{
            .value = input_engine->GetButton(identifier, button),
            .inverted = inverted,
            .toggle = toggle,
        };
        return {
            .analog = analog_status,
            .pressed = button_status,
        };
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Trigger,
            .trigger_status = GetStatus(),
        };

        if (status.trigger_status.analog.raw_value != last_axis_value ||
            status.trigger_status.pressed.value != last_button_value) {
            last_axis_value = status.trigger_status.analog.raw_value;
            last_button_value = status.trigger_status.pressed.value;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    const int button;
    const bool toggle;
    const bool inverted;
    const int axis;
    const Common::Input::AnalogProperties properties;
    int callback_key_button;
    int axis_callback_key;
    bool last_button_value;
    float last_axis_value;
    InputEngine* input_engine;
};

class InputFromAnalog final : public Common::Input::InputDevice {
public:
    explicit InputFromAnalog(PadIdentifier identifier_, int axis_,
                             Common::Input::AnalogProperties properties_,
                             InputEngine* input_engine_)
        : identifier(identifier_), axis(axis_), properties(properties_),
          input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis,
            .callback = engine_callback,
        };
        last_axis_value = 0.0f;
        callback_key = input_engine->SetCallback(input_identifier);
    }

    ~InputFromAnalog() override {
        input_engine->DeleteCallback(callback_key);
    }

    Common::Input::AnalogStatus GetStatus() const {
        return {
            .raw_value = input_engine->GetAxis(identifier, axis),
            .properties = properties,
        };
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Analog,
            .analog_status = GetStatus(),
        };

        if (status.analog_status.raw_value != last_axis_value) {
            last_axis_value = status.analog_status.raw_value;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    const int axis;
    const Common::Input::AnalogProperties properties;
    int callback_key;
    float last_axis_value;
    InputEngine* input_engine;
};

class InputFromBattery final : public Common::Input::InputDevice {
public:
    explicit InputFromBattery(PadIdentifier identifier_, InputEngine* input_engine_)
        : identifier(identifier_), input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Battery,
            .index = 0,
            .callback = engine_callback,
        };
        last_battery_value = Common::Input::BatteryStatus::Charging;
        callback_key = input_engine->SetCallback(input_identifier);
    }

    ~InputFromBattery() override {
        input_engine->DeleteCallback(callback_key);
    }

    Common::Input::BatteryStatus GetStatus() const {
        return input_engine->GetBattery(identifier);
    }

    void ForceUpdate() override {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Battery,
            .battery_status = GetStatus(),
        };

        last_battery_value = status.battery_status;
        TriggerOnChange(status);
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Battery,
            .battery_status = GetStatus(),
        };

        if (status.battery_status != last_battery_value) {
            last_battery_value = status.battery_status;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    int callback_key;
    Common::Input::BatteryStatus last_battery_value;
    InputEngine* input_engine;
};

class InputFromColor final : public Common::Input::InputDevice {
public:
    explicit InputFromColor(PadIdentifier identifier_, InputEngine* input_engine_)
        : identifier(identifier_), input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Color,
            .index = 0,
            .callback = engine_callback,
        };
        last_color_value = {};
        callback_key = input_engine->SetCallback(input_identifier);
    }

    ~InputFromColor() override {
        input_engine->DeleteCallback(callback_key);
    }

    Common::Input::BodyColorStatus GetStatus() const {
        return input_engine->GetColor(identifier);
    }

    void ForceUpdate() override {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Color,
            .color_status = GetStatus(),
        };

        last_color_value = status.color_status;
        TriggerOnChange(status);
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Color,
            .color_status = GetStatus(),
        };

        if (status.color_status.body != last_color_value.body) {
            last_color_value = status.color_status;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    int callback_key;
    Common::Input::BodyColorStatus last_color_value;
    InputEngine* input_engine;
};

class InputFromMotion final : public Common::Input::InputDevice {
public:
    explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_,
                             InputEngine* input_engine_)
        : identifier(identifier_), motion_sensor(motion_sensor_), gyro_threshold(gyro_threshold_),
          input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Motion,
            .index = motion_sensor,
            .callback = engine_callback,
        };
        callback_key = input_engine->SetCallback(input_identifier);
    }

    ~InputFromMotion() override {
        input_engine->DeleteCallback(callback_key);
    }

    Common::Input::MotionStatus GetStatus() const {
        const auto basic_motion = input_engine->GetMotion(identifier, motion_sensor);
        Common::Input::MotionStatus status{};
        const Common::Input::AnalogProperties properties = {
            .deadzone = 0.0f,
            .range = 1.0f,
            .threshold = gyro_threshold,
            .offset = 0.0f,
        };
        status.accel.x = {.raw_value = basic_motion.accel_x, .properties = properties};
        status.accel.y = {.raw_value = basic_motion.accel_y, .properties = properties};
        status.accel.z = {.raw_value = basic_motion.accel_z, .properties = properties};
        status.gyro.x = {.raw_value = basic_motion.gyro_x, .properties = properties};
        status.gyro.y = {.raw_value = basic_motion.gyro_y, .properties = properties};
        status.gyro.z = {.raw_value = basic_motion.gyro_z, .properties = properties};
        status.delta_timestamp = basic_motion.delta_timestamp;
        return status;
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Motion,
            .motion_status = GetStatus(),
        };

        TriggerOnChange(status);
    }

private:
    const PadIdentifier identifier;
    const int motion_sensor;
    const float gyro_threshold;
    int callback_key;
    InputEngine* input_engine;
};

class InputFromAxisMotion final : public Common::Input::InputDevice {
public:
    explicit InputFromAxisMotion(PadIdentifier identifier_, int axis_x_, int axis_y_, int axis_z_,
                                 Common::Input::AnalogProperties properties_x_,
                                 Common::Input::AnalogProperties properties_y_,
                                 Common::Input::AnalogProperties properties_z_,
                                 InputEngine* input_engine_)
        : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), axis_z(axis_z_),
          properties_x(properties_x_), properties_y(properties_y_), properties_z(properties_z_),
          input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier x_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis_x,
            .callback = engine_callback,
        };
        const InputIdentifier y_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis_y,
            .callback = engine_callback,
        };
        const InputIdentifier z_input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Analog,
            .index = axis_z,
            .callback = engine_callback,
        };
        last_axis_x_value = 0.0f;
        last_axis_y_value = 0.0f;
        last_axis_z_value = 0.0f;
        callback_key_x = input_engine->SetCallback(x_input_identifier);
        callback_key_y = input_engine->SetCallback(y_input_identifier);
        callback_key_z = input_engine->SetCallback(z_input_identifier);
    }

    ~InputFromAxisMotion() override {
        input_engine->DeleteCallback(callback_key_x);
        input_engine->DeleteCallback(callback_key_y);
        input_engine->DeleteCallback(callback_key_z);
    }

    Common::Input::MotionStatus GetStatus() const {
        Common::Input::MotionStatus status{};
        status.gyro.x = {
            .raw_value = input_engine->GetAxis(identifier, axis_x),
            .properties = properties_x,
        };
        status.gyro.y = {
            .raw_value = input_engine->GetAxis(identifier, axis_y),
            .properties = properties_y,
        };
        status.gyro.z = {
            .raw_value = input_engine->GetAxis(identifier, axis_z),
            .properties = properties_z,
        };
        status.delta_timestamp = 1000;
        status.force_update = true;
        return status;
    }

    void ForceUpdate() override {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Motion,
            .motion_status = GetStatus(),
        };

        last_axis_x_value = status.motion_status.gyro.x.raw_value;
        last_axis_y_value = status.motion_status.gyro.y.raw_value;
        last_axis_z_value = status.motion_status.gyro.z.raw_value;
        TriggerOnChange(status);
    }

    void OnChange() {
        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Motion,
            .motion_status = GetStatus(),
        };

        if (status.motion_status.gyro.x.raw_value != last_axis_x_value ||
            status.motion_status.gyro.y.raw_value != last_axis_y_value ||
            status.motion_status.gyro.z.raw_value != last_axis_z_value) {
            last_axis_x_value = status.motion_status.gyro.x.raw_value;
            last_axis_y_value = status.motion_status.gyro.y.raw_value;
            last_axis_z_value = status.motion_status.gyro.z.raw_value;
            TriggerOnChange(status);
        }
    }

private:
    const PadIdentifier identifier;
    const int axis_x;
    const int axis_y;
    const int axis_z;
    const Common::Input::AnalogProperties properties_x;
    const Common::Input::AnalogProperties properties_y;
    const Common::Input::AnalogProperties properties_z;
    int callback_key_x;
    int callback_key_y;
    int callback_key_z;
    float last_axis_x_value;
    float last_axis_y_value;
    float last_axis_z_value;
    InputEngine* input_engine;
};

class InputFromCamera final : public Common::Input::InputDevice {
public:
    explicit InputFromCamera(PadIdentifier identifier_, InputEngine* input_engine_)
        : identifier(identifier_), input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Camera,
            .index = 0,
            .callback = engine_callback,
        };
        callback_key = input_engine->SetCallback(input_identifier);
    }

    ~InputFromCamera() override {
        input_engine->DeleteCallback(callback_key);
    }

    Common::Input::CameraStatus GetStatus() const {
        return input_engine->GetCamera(identifier);
    }

    void ForceUpdate() override {
        OnChange();
    }

    void OnChange() {
        const auto camera_status = GetStatus();

        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::IrSensor,
            .camera_status = camera_status.format,
            .raw_data = camera_status.data,
        };

        TriggerOnChange(status);
    }

private:
    const PadIdentifier identifier;
    int callback_key;
    InputEngine* input_engine;
};

class InputFromNfc final : public Common::Input::InputDevice {
public:
    explicit InputFromNfc(PadIdentifier identifier_, InputEngine* input_engine_)
        : identifier(identifier_), input_engine(input_engine_) {
        UpdateCallback engine_callback{[this]() { OnChange(); }};
        const InputIdentifier input_identifier{
            .identifier = identifier,
            .type = EngineInputType::Nfc,
            .index = 0,
            .callback = engine_callback,
        };
        callback_key = input_engine->SetCallback(input_identifier);
    }

    ~InputFromNfc() override {
        input_engine->DeleteCallback(callback_key);
    }

    Common::Input::NfcStatus GetStatus() const {
        return input_engine->GetNfc(identifier);
    }

    void ForceUpdate() override {
        OnChange();
    }

    void OnChange() {
        const auto nfc_status = GetStatus();

        const Common::Input::CallbackStatus status{
            .type = Common::Input::InputType::Nfc,
            .nfc_status = nfc_status,
        };

        TriggerOnChange(status);
    }

private:
    const PadIdentifier identifier;
    int callback_key;
    InputEngine* input_engine;
};

class OutputFromIdentifier final : public Common::Input::OutputDevice {
public:
    explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
        : identifier(identifier_), input_engine(input_engine_) {}

    Common::Input::DriverResult SetLED(const Common::Input::LedStatus& led_status) override {
        return input_engine->SetLeds(identifier, led_status);
    }

    Common::Input::DriverResult SetVibration(
        const Common::Input::VibrationStatus& vibration_status) override {
        return input_engine->SetVibration(identifier, vibration_status);
    }

    bool IsVibrationEnabled() override {
        return input_engine->IsVibrationEnabled(identifier);
    }

    Common::Input::DriverResult SetPollingMode(Common::Input::PollingMode polling_mode) override {
        return input_engine->SetPollingMode(identifier, polling_mode);
    }

    Common::Input::DriverResult SetCameraFormat(
        Common::Input::CameraFormat camera_format) override {
        return input_engine->SetCameraFormat(identifier, camera_format);
    }

    Common::Input::NfcState SupportsNfc() const override {
        return input_engine->SupportsNfc(identifier);
    }

    Common::Input::NfcState StartNfcPolling() override {
        return input_engine->StartNfcPolling(identifier);
    }

    Common::Input::NfcState StopNfcPolling() override {
        return input_engine->StopNfcPolling(identifier);
    }

    Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) override {
        return input_engine->ReadAmiiboData(identifier, out_data);
    }

    Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override {
        return input_engine->WriteNfcData(identifier, data);
    }

    Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
                                           Common::Input::MifareRequest& out_data) override {
        return input_engine->ReadMifareData(identifier, request, out_data);
    }

    Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) override {
        return input_engine->WriteMifareData(identifier, request);
    }

private:
    const PadIdentifier identifier;
    InputEngine* input_engine;
};

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    const auto button_id = params.Get("button", 0);
    const auto keyboard_key = params.Get("code", 0);
    const auto toggle = params.Get("toggle", false) != 0;
    const auto inverted = params.Get("inverted", false) != 0;
    const auto turbo = params.Get("turbo", false) != 0;
    input_engine->PreSetController(identifier);
    input_engine->PreSetButton(identifier, button_id);
    input_engine->PreSetButton(identifier, keyboard_key);
    if (keyboard_key != 0) {
        return std::make_unique<InputFromButton>(identifier, keyboard_key, turbo, toggle, inverted,
                                                 input_engine.get());
    }
    return std::make_unique<InputFromButton>(identifier, button_id, turbo, toggle, inverted,
                                             input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    const auto button_id = params.Get("hat", 0);
    const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
    const auto toggle = params.Get("toggle", false) != 0;
    const auto inverted = params.Get("inverted", false) != 0;
    const auto turbo = params.Get("turbo", false) != 0;

    input_engine->PreSetController(identifier);
    input_engine->PreSetHatButton(identifier, button_id);
    return std::make_unique<InputFromHatButton>(identifier, button_id, direction, turbo, toggle,
                                                inverted, input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(
    const Common::ParamPackage& params) {
    const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f);
    const auto range = std::clamp(params.Get("range", 0.95f), 0.25f, 1.50f);
    const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    const auto axis_x = params.Get("axis_x", 0);
    const Common::Input::AnalogProperties properties_x = {
        .deadzone = deadzone,
        .range = range,
        .threshold = threshold,
        .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert_x", "+") == "-",
    };

    const auto axis_y = params.Get("axis_y", 1);
    const Common::Input::AnalogProperties properties_y = {
        .deadzone = deadzone,
        .range = range,
        .threshold = threshold,
        .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert_y", "+") != "+",
    };
    input_engine->PreSetController(identifier);
    input_engine->PreSetAxis(identifier, axis_x);
    input_engine->PreSetAxis(identifier, axis_y);
    return std::make_unique<InputFromStick>(identifier, axis_x, axis_y, properties_x, properties_y,
                                            input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    const auto axis = params.Get("axis", 0);
    const Common::Input::AnalogProperties properties = {
        .deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f),
        .range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f),
        .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
        .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert", "+") == "-",
        .inverted_button = params.Get("inverted", false) != 0,
        .toggle = params.Get("toggle", false) != 0,
    };
    input_engine->PreSetController(identifier);
    input_engine->PreSetAxis(identifier, axis);
    return std::make_unique<InputFromAnalog>(identifier, axis, properties, input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    const auto button = params.Get("button", 0);
    const auto toggle = params.Get("toggle", false) != 0;
    const auto inverted = params.Get("inverted", false) != 0;

    const auto axis = params.Get("axis", 0);
    const Common::Input::AnalogProperties properties = {
        .deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f),
        .range = std::clamp(params.Get("range", 1.0f), 0.25f, 2.50f),
        .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
        .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert", false) != 0,
    };
    input_engine->PreSetController(identifier);
    input_engine->PreSetAxis(identifier, axis);
    input_engine->PreSetButton(identifier, button);
    return std::make_unique<InputFromTrigger>(identifier, button, toggle, inverted, axis,
                                              properties, input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
    const Common::ParamPackage& params) {
    const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
    const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
    const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    const auto button = params.Get("button", 0);
    const auto toggle = params.Get("toggle", false) != 0;
    const auto inverted = params.Get("inverted", false) != 0;

    const auto axis_x = params.Get("axis_x", 0);
    const Common::Input::AnalogProperties properties_x = {
        .deadzone = deadzone,
        .range = range,
        .threshold = threshold,
        .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert_x", "+") == "-",
    };

    const auto axis_y = params.Get("axis_y", 1);
    const Common::Input::AnalogProperties properties_y = {
        .deadzone = deadzone,
        .range = range,
        .threshold = threshold,
        .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert_y", false) != 0,
    };
    input_engine->PreSetController(identifier);
    input_engine->PreSetAxis(identifier, axis_x);
    input_engine->PreSetAxis(identifier, axis_y);
    input_engine->PreSetButton(identifier, button);
    return std::make_unique<InputFromTouch>(identifier, button, toggle, inverted, axis_x, axis_y,
                                            properties_x, properties_y, input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    input_engine->PreSetController(identifier);
    return std::make_unique<InputFromBattery>(identifier, input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateColorDevice(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    input_engine->PreSetController(identifier);
    return std::make_unique<InputFromColor>(identifier, input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
    Common::ParamPackage params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    if (params.Has("motion")) {
        const auto motion_sensor = params.Get("motion", 0);
        const auto gyro_threshold = params.Get("threshold", 0.007f);
        input_engine->PreSetController(identifier);
        input_engine->PreSetMotion(identifier, motion_sensor);
        return std::make_unique<InputFromMotion>(identifier, motion_sensor, gyro_threshold,
                                                 input_engine.get());
    }

    const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f);
    const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
    const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);

    const auto axis_x = params.Get("axis_x", 0);
    const Common::Input::AnalogProperties properties_x = {
        .deadzone = deadzone,
        .range = range,
        .threshold = threshold,
        .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert_x", "+") == "-",
    };

    const auto axis_y = params.Get("axis_y", 1);
    const Common::Input::AnalogProperties properties_y = {
        .deadzone = deadzone,
        .range = range,
        .threshold = threshold,
        .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert_y", "+") != "+",
    };

    const auto axis_z = params.Get("axis_z", 1);
    const Common::Input::AnalogProperties properties_z = {
        .deadzone = deadzone,
        .range = range,
        .threshold = threshold,
        .offset = std::clamp(params.Get("offset_z", 0.0f), -1.0f, 1.0f),
        .inverted = params.Get("invert_z", "+") != "+",
    };
    input_engine->PreSetController(identifier);
    input_engine->PreSetAxis(identifier, axis_x);
    input_engine->PreSetAxis(identifier, axis_y);
    input_engine->PreSetAxis(identifier, axis_z);
    return std::make_unique<InputFromAxisMotion>(identifier, axis_x, axis_y, axis_z, properties_x,
                                                 properties_y, properties_z, input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    input_engine->PreSetController(identifier);
    return std::make_unique<InputFromCamera>(identifier, input_engine.get());
}

std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateNfcDevice(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    input_engine->PreSetController(identifier);
    return std::make_unique<InputFromNfc>(identifier, input_engine.get());
}

InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
    : input_engine(std::move(input_engine_)) {}

std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
    const Common::ParamPackage& params) {
    if (params.Has("battery")) {
        return CreateBatteryDevice(params);
    }
    if (params.Has("color")) {
        return CreateColorDevice(params);
    }
    if (params.Has("camera")) {
        return CreateCameraDevice(params);
    }
    if (params.Has("nfc")) {
        return CreateNfcDevice(params);
    }
    if (params.Has("button") && params.Has("axis")) {
        return CreateTriggerDevice(params);
    }
    if (params.Has("button") && params.Has("axis_x") && params.Has("axis_y")) {
        return CreateTouchDevice(params);
    }
    if (params.Has("button") || params.Has("code")) {
        return CreateButtonDevice(params);
    }
    if (params.Has("hat")) {
        return CreateHatButtonDevice(params);
    }
    if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
        return CreateMotionDevice(params);
    }
    if (params.Has("motion")) {
        return CreateMotionDevice(params);
    }
    if (params.Has("axis_x") && params.Has("axis_y")) {
        return CreateStickDevice(params);
    }
    if (params.Has("axis")) {
        return CreateAnalogDevice(params);
    }
    LOG_ERROR(Input, "Invalid parameters given");
    return std::make_unique<DummyInput>();
}

OutputFactory::OutputFactory(std::shared_ptr<InputEngine> input_engine_)
    : input_engine(std::move(input_engine_)) {}

std::unique_ptr<Common::Input::OutputDevice> OutputFactory::Create(
    const Common::ParamPackage& params) {
    const PadIdentifier identifier = {
        .guid = Common::UUID{params.Get("guid", "")},
        .port = static_cast<std::size_t>(params.Get("port", 0)),
        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
    };

    input_engine->PreSetController(identifier);
    return std::make_unique<OutputFromIdentifier>(identifier, input_engine.get());
}

} // namespace InputCommon

repo_name: litucks/torzu

license: mit


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

#pragma once

namespace Input {
class InputDevice;

template <typename InputDevice>
class Factory;
}; // namespace Input

namespace InputCommon {
class InputEngine;

class OutputFactory final : public Common::Input::Factory<Common::Input::OutputDevice> {
public:
    explicit OutputFactory(std::shared_ptr<InputEngine> input_engine_);

    /**
     * Creates an output device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "guid" text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique output device with the parameters specified
     */
    std::unique_ptr<Common::Input::OutputDevice> Create(
        const Common::ParamPackage& params) override;

private:
    std::shared_ptr<InputEngine> input_engine;
};

/**
 * An Input factory. It receives input events and forward them to all input devices it created.
 */
class InputFactory final : public Common::Input::Factory<Common::Input::InputDevice> {
public:
    explicit InputFactory(std::shared_ptr<InputEngine> input_engine_);

    /**
     * Creates an input device from the parameters given. Identifies the type of input to be
     * returned if it contains the following parameters:
     * - button: Contains "button" or "code"
     * - hat_button: Contains "hat"
     * - analog: Contains "axis"
     * - trigger: Contains "button" and  "axis"
     * - stick: Contains "axis_x" and "axis_y"
     * - motion: Contains "axis_x", "axis_y" and "axis_z"
     * - motion: Contains "motion"
     * - touch: Contains "button", "axis_x" and "axis_y"
     * - battery: Contains "battery"
     * - output: Contains "output"
     * @param params contains parameters for creating the device:
     *               - "code": the code of the keyboard key to bind with the input
     *               - "button": same as "code" but for controller buttons
     *               - "hat": similar as "button" but it's a group of hat buttons from SDL
     *               - "axis": the axis number of the axis to bind with the input
     *               - "motion": the motion number of the motion to bind with the input
     *               - "axis_x": same as axis but specifying horizontal direction
     *               - "axis_y": same as axis but specifying vertical direction
     *               - "axis_z": same as axis but specifying forward direction
     *               - "battery": Only used as a placeholder to set the input type
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;

private:
    /**
     * Creates a button device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "code": the code of the keyboard key to bind with the input
     *               - "button": same as "code" but for controller buttons
     *               - "toggle": press once to enable, press again to disable
     *               - "inverted": inverts the output of the button
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateButtonDevice(
        const Common::ParamPackage& params);

    /**
     * Creates a hat button device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "button": the controller hat id to bind with the input
     *               - "direction": the direction id to be detected
     *               - "toggle": press once to enable, press again to disable
     *               - "inverted": inverts the output of the button
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateHatButtonDevice(
        const Common::ParamPackage& params);

    /**
     * Creates a stick device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "axis_x": the controller horizontal axis id to bind with the input
     *               - "axis_y": the controller vertical axis id to bind with the input
     *               - "deadzone": the minimum required value to be detected
     *               - "range": the maximum value required to reach 100%
     *               - "threshold": the minimum required value to considered pressed
     *               - "offset_x": the amount of offset in the x axis
     *               - "offset_y": the amount of offset in the y axis
     *               - "invert_x": inverts the sign of the horizontal axis
     *               - "invert_y": inverts the sign of the vertical axis
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateStickDevice(
        const Common::ParamPackage& params);

    /**
     * Creates an analog device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "axis": the controller axis id to bind with the input
     *               - "deadzone": the minimum required value to be detected
     *               - "range": the maximum value required to reach 100%
     *               - "threshold": the minimum required value to considered pressed
     *               - "offset": the amount of offset in the axis
     *               - "invert": inverts the sign of the axis
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateAnalogDevice(
        const Common::ParamPackage& params);

    /**
     * Creates a trigger device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "button": the controller hat id to bind with the input
     *               - "direction": the direction id to be detected
     *               - "toggle": press once to enable, press again to disable
     *               - "inverted": inverts the output of the button
     *               - "axis": the controller axis id to bind with the input
     *               - "deadzone": the minimum required value to be detected
     *               - "range": the maximum value required to reach 100%
     *               - "threshold": the minimum required value to considered pressed
     *               - "offset": the amount of offset in the axis
     *               - "invert": inverts the sign of the axis
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateTriggerDevice(
        const Common::ParamPackage& params);

    /**
     * Creates a touch device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "button": the controller hat id to bind with the input
     *               - "direction": the direction id to be detected
     *               - "toggle": press once to enable, press again to disable
     *               - "inverted": inverts the output of the button
     *               - "axis_x": the controller horizontal axis id to bind with the input
     *               - "axis_y": the controller vertical axis id to bind with the input
     *               - "deadzone": the minimum required value to be detected
     *               - "range": the maximum value required to reach 100%
     *               - "threshold": the minimum required value to considered pressed
     *               - "offset_x": the amount of offset in the x axis
     *               - "offset_y": the amount of offset in the y axis
     *               - "invert_x": inverts the sign of the horizontal axis
     *               - "invert_y": inverts the sign of the vertical axis
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateTouchDevice(
        const Common::ParamPackage& params);

    /**
     * Creates a battery device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice(
        const Common::ParamPackage& params);

    /**
     * Creates a color device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateColorDevice(
        const Common::ParamPackage& params);

    /**
     * Creates a motion device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "axis_x": the controller horizontal axis id to bind with the input
     *               - "axis_y": the controller vertical axis id to bind with the input
     *               - "axis_z": the controller forward axis id to bind with the input
     *               - "deadzone": the minimum required value to be detected
     *               - "range": the maximum value required to reach 100%
     *               - "offset_x": the amount of offset in the x axis
     *               - "offset_y": the amount of offset in the y axis
     *               - "offset_z": the amount of offset in the z axis
     *               - "invert_x": inverts the sign of the horizontal axis
     *               - "invert_y": inverts the sign of the vertical axis
     *               - "invert_z": inverts the sign of the forward axis
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);

    /**
     * Creates a camera device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
        const Common::ParamPackage& params);

    /**
     * Creates a nfc device from the parameters given.
     * @param params contains parameters for creating the device:
     *               - "guid": text string for identifying controllers
     *               - "port": port of the connected device
     *               - "pad": slot of the connected controller
     * @returns a unique input device with the parameters specified
     */
    std::unique_ptr<Common::Input::InputDevice> CreateNfcDevice(const Common::ParamPackage& params);

    std::shared_ptr<InputEngine> input_engine;
};
} // namespace InputCommon


code:
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <memory>
#include "common/input.h"
#include "common/param_package.h"
#include "input_common/drivers/camera.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
#include "input_common/drivers/tas_input.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/udp_client.h"
#include "input_common/drivers/virtual_amiibo.h"
#include "input_common/drivers/virtual_gamepad.h"
#include "input_common/helpers/stick_from_buttons.h"
#include "input_common/helpers/touch_from_buttons.h"
#include "input_common/input_engine.h"
#include "input_common/input_mapping.h"
#include "input_common/input_poller.h"
#include "input_common/main.h"

#ifdef HAVE_LIBUSB
#include "input_common/drivers/gc_adapter.h"
#endif
#ifdef HAVE_SDL2
#include "input_common/drivers/joycon.h"
#include "input_common/drivers/sdl_driver.h"
#endif

#ifdef ANDROID
#include "input_common/drivers/android.h"
#endif

namespace InputCommon {

/// Dummy engine to get periodic updates
class UpdateEngine final : public InputEngine {
public:
    explicit UpdateEngine(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
        PreSetController(identifier);
    }

    void PumpEvents() {
        SetButton(identifier, 0, last_state);
        last_state = !last_state;
    }

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

    bool last_state{};
};

struct InputSubsystem::Impl {
    template <typename Engine>
    void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {
        MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }};

        engine = std::make_shared<Engine>(name);
        engine->SetMappingCallback(mapping_callback);

        std::shared_ptr<InputFactory> input_factory = std::make_shared<InputFactory>(engine);
        std::shared_ptr<OutputFactory> output_factory = std::make_shared<OutputFactory>(engine);
        Common::Input::RegisterInputFactory(engine->GetEngineName(), std::move(input_factory));
        Common::Input::RegisterOutputFactory(engine->GetEngineName(), std::move(output_factory));
    }

    void Initialize() {
        mapping_factory = std::make_shared<MappingFactory>();

        RegisterEngine("updater", update_engine);
        RegisterEngine("keyboard", keyboard);
        RegisterEngine("mouse", mouse);
        RegisterEngine("touch", touch_screen);
#ifdef HAVE_LIBUSB
        RegisterEngine("gcpad", gcadapter);
#endif
        RegisterEngine("cemuhookudp", udp_client);
        RegisterEngine("tas", tas_input);
        RegisterEngine("camera", camera);
#ifdef ANDROID
        RegisterEngine("android", android);
#endif
        RegisterEngine("virtual_amiibo", virtual_amiibo);
        RegisterEngine("virtual_gamepad", virtual_gamepad);
#ifdef HAVE_SDL2
        RegisterEngine("sdl", sdl);
        RegisterEngine("joycon", joycon);
#endif

        Common::Input::RegisterInputFactory("touch_from_button",
                                            std::make_shared<TouchFromButton>());
        Common::Input::RegisterInputFactory("analog_from_button",
                                            std::make_shared<StickFromButton>());
    }

    template <typename Engine>
    void UnregisterEngine(std::shared_ptr<Engine>& engine) {
        Common::Input::UnregisterInputFactory(engine->GetEngineName());
        Common::Input::UnregisterOutputFactory(engine->GetEngineName());
        engine.reset();
    }

    void Shutdown() {
        UnregisterEngine(update_engine);
        UnregisterEngine(keyboard);
        UnregisterEngine(mouse);
        UnregisterEngine(touch_screen);
#ifdef HAVE_LIBUSB
        UnregisterEngine(gcadapter);
#endif
        UnregisterEngine(udp_client);
        UnregisterEngine(tas_input);
        UnregisterEngine(camera);
#ifdef ANDROID
        UnregisterEngine(android);
#endif
        UnregisterEngine(virtual_amiibo);
        UnregisterEngine(virtual_gamepad);
#ifdef HAVE_SDL2
        UnregisterEngine(sdl);
        UnregisterEngine(joycon);
#endif

        Common::Input::UnregisterInputFactory("touch_from_button");
        Common::Input::UnregisterInputFactory("analog_from_button");
    }

    [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
        std::vector<Common::ParamPackage> devices = {
            Common::ParamPackage{{"display", "Any"}, {"engine", "any"}},
        };

#ifndef ANDROID
        auto keyboard_devices = keyboard->GetInputDevices();
        devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
        auto mouse_devices = mouse->GetInputDevices();
        devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
#endif
#ifdef ANDROID
        auto android_devices = android->GetInputDevices();
        devices.insert(devices.end(), android_devices.begin(), android_devices.end());
#endif
#ifdef HAVE_LIBUSB
        auto gcadapter_devices = gcadapter->GetInputDevices();
        devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
#endif
        auto udp_devices = udp_client->GetInputDevices();
        devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
#ifdef HAVE_SDL2
        auto joycon_devices = joycon->GetInputDevices();
        devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
        auto sdl_devices = sdl->GetInputDevices();
        devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
#endif

        return devices;
    }

    [[nodiscard]] std::shared_ptr<InputEngine> GetInputEngine(
        const Common::ParamPackage& params) const {
        if (!params.Has("engine") || params.Get("engine", "") == "any") {
            return nullptr;
        }
        const std::string engine = params.Get("engine", "");
        if (engine == keyboard->GetEngineName()) {
            return keyboard;
        }
        if (engine == mouse->GetEngineName()) {
            return mouse;
        }
#ifdef ANDROID
        if (engine == android->GetEngineName()) {
            return android;
        }
#endif
#ifdef HAVE_LIBUSB
        if (engine == gcadapter->GetEngineName()) {
            return gcadapter;
        }
#endif
        if (engine == udp_client->GetEngineName()) {
            return udp_client;
        }
#ifdef HAVE_SDL2
        if (engine == sdl->GetEngineName()) {
            return sdl;
        }
        if (engine == joycon->GetEngineName()) {
            return joycon;
        }
#endif
        return nullptr;
    }

    [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
        const Common::ParamPackage& params) const {
        const auto input_engine = GetInputEngine(params);

        if (input_engine == nullptr) {
            return {};
        }

        return input_engine->GetAnalogMappingForDevice(params);
    }

    [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
        const Common::ParamPackage& params) const {
        const auto input_engine = GetInputEngine(params);

        if (input_engine == nullptr) {
            return {};
        }

        return input_engine->GetButtonMappingForDevice(params);
    }

    [[nodiscard]] MotionMapping GetMotionMappingForDevice(
        const Common::ParamPackage& params) const {
        const auto input_engine = GetInputEngine(params);

        if (input_engine == nullptr) {
            return {};
        }

        return input_engine->GetMotionMappingForDevice(params);
    }

    Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
        if (!params.Has("engine") || params.Get("engine", "") == "any") {
            return Common::Input::ButtonNames::Undefined;
        }
        const auto input_engine = GetInputEngine(params);

        if (input_engine == nullptr) {
            return Common::Input::ButtonNames::Invalid;
        }

        return input_engine->GetUIName(params);
    }

    bool IsStickInverted(const Common::ParamPackage& params) {
        const auto input_engine = GetInputEngine(params);

        if (input_engine == nullptr) {
            return false;
        }

        return input_engine->IsStickInverted(params);
    }

    bool IsController(const Common::ParamPackage& params) {
        const std::string engine = params.Get("engine", "");
        if (engine == mouse->GetEngineName()) {
            return true;
        }
#ifdef ANDROID
        if (engine == android->GetEngineName()) {
            return true;
        }
#endif
#ifdef HAVE_LIBUSB
        if (engine == gcadapter->GetEngineName()) {
            return true;
        }
#endif
        if (engine == udp_client->GetEngineName()) {
            return true;
        }
        if (engine == tas_input->GetEngineName()) {
            return true;
        }
        if (engine == virtual_gamepad->GetEngineName()) {
            return true;
        }
#ifdef HAVE_SDL2
        if (engine == sdl->GetEngineName()) {
            return true;
        }
        if (engine == joycon->GetEngineName()) {
            return true;
        }
#endif
        return false;
    }

    void BeginConfiguration() {
        keyboard->BeginConfiguration();
        mouse->BeginConfiguration();
#ifdef ANDROID
        android->BeginConfiguration();
#endif
#ifdef HAVE_LIBUSB
        gcadapter->BeginConfiguration();
#endif
        udp_client->BeginConfiguration();
#ifdef HAVE_SDL2
        sdl->BeginConfiguration();
        joycon->BeginConfiguration();
#endif
    }

    void EndConfiguration() {
        keyboard->EndConfiguration();
        mouse->EndConfiguration();
#ifdef ANDROID
        android->EndConfiguration();
#endif
#ifdef HAVE_LIBUSB
        gcadapter->EndConfiguration();
#endif
        udp_client->EndConfiguration();
#ifdef HAVE_SDL2
        sdl->EndConfiguration();
        joycon->EndConfiguration();
#endif
    }

    void PumpEvents() const {
        update_engine->PumpEvents();
#ifdef HAVE_SDL2
        sdl->PumpEvents();
#endif
    }

    void RegisterInput(const MappingData& data) {
        mapping_factory->RegisterInput(data);
    }

    std::shared_ptr<MappingFactory> mapping_factory;

    std::shared_ptr<UpdateEngine> update_engine;
    std::shared_ptr<Keyboard> keyboard;
    std::shared_ptr<Mouse> mouse;
    std::shared_ptr<TouchScreen> touch_screen;
    std::shared_ptr<TasInput::Tas> tas_input;
    std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
    std::shared_ptr<Camera> camera;
    std::shared_ptr<VirtualAmiibo> virtual_amiibo;
    std::shared_ptr<VirtualGamepad> virtual_gamepad;

#ifdef HAVE_LIBUSB
    std::shared_ptr<GCAdapter> gcadapter;
#endif

#ifdef HAVE_SDL2
    std::shared_ptr<SDLDriver> sdl;
    std::shared_ptr<Joycons> joycon;
#endif

#ifdef ANDROID
    std::shared_ptr<Android> android;
#endif
};

InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}

InputSubsystem::~InputSubsystem() = default;

void InputSubsystem::Initialize() {
    impl->Initialize();
}

void InputSubsystem::Shutdown() {
    impl->Shutdown();
}

Keyboard* InputSubsystem::GetKeyboard() {
    return impl->keyboard.get();
}

const Keyboard* InputSubsystem::GetKeyboard() const {
    return impl->keyboard.get();
}

Mouse* InputSubsystem::GetMouse() {
    return impl->mouse.get();
}

const Mouse* InputSubsystem::GetMouse() const {
    return impl->mouse.get();
}

TouchScreen* InputSubsystem::GetTouchScreen() {
    return impl->touch_screen.get();
}

const TouchScreen* InputSubsystem::GetTouchScreen() const {
    return impl->touch_screen.get();
}

TasInput::Tas* InputSubsystem::GetTas() {
    return impl->tas_input.get();
}

const TasInput::Tas* InputSubsystem::GetTas() const {
    return impl->tas_input.get();
}

Camera* InputSubsystem::GetCamera() {
    return impl->camera.get();
}

const Camera* InputSubsystem::GetCamera() const {
    return impl->camera.get();
}

#ifdef ANDROID
Android* InputSubsystem::GetAndroid() {
    return impl->android.get();
}

const Android* InputSubsystem::GetAndroid() const {
    return impl->android.get();
}
#endif

VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
    return impl->virtual_amiibo.get();
}

const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {
    return impl->virtual_amiibo.get();
}

VirtualGamepad* InputSubsystem::GetVirtualGamepad() {
    return impl->virtual_gamepad.get();
}

const VirtualGamepad* InputSubsystem::GetVirtualGamepad() const {
    return impl->virtual_gamepad.get();
}

std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
    return impl->GetInputDevices();
}

AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
    return impl->GetAnalogMappingForDevice(device);
}

ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
    return impl->GetButtonMappingForDevice(device);
}

MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPackage& device) const {
    return impl->GetMotionMappingForDevice(device);
}

Common::Input::ButtonNames InputSubsystem::GetButtonName(const Common::ParamPackage& params) const {
    return impl->GetButtonName(params);
}

bool InputSubsystem::IsController(const Common::ParamPackage& params) const {
    return impl->IsController(params);
}

bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
    if (params.Has("axis_x") && params.Has("axis_y")) {
        return impl->IsStickInverted(params);
    }
    return false;
}

void InputSubsystem::ReloadInputDevices() {
    impl->udp_client.get()->ReloadSockets();
}

void InputSubsystem::BeginMapping(Polling::InputType type) {
    impl->BeginConfiguration();
    impl->mapping_factory->BeginMapping(type);
}

Common::ParamPackage InputSubsystem::GetNextInput() const {
    return impl->mapping_factory->GetNextInput();
}

void InputSubsystem::StopMapping() const {
    impl->EndConfiguration();
    impl->mapping_factory->StopMapping();
}

void InputSubsystem::PumpEvents() const {
    impl->PumpEvents();
}

std::string GenerateKeyboardParam(int key_code) {
    Common::ParamPackage param;
    param.Set("engine", "keyboard");
    param.Set("code", key_code);
    param.Set("toggle", false);
    return param.Serialize();
}

std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
                                        int key_modifier, float modifier_scale) {
    Common::ParamPackage circle_pad_param{
        {"engine", "analog_from_button"},
        {"up", GenerateKeyboardParam(key_up)},
        {"down", GenerateKeyboardParam(key_down)},
        {"left", GenerateKeyboardParam(key_left)},
        {"right", GenerateKeyboardParam(key_right)},
        {"modifier", GenerateKeyboardParam(key_modifier)},
        {"modifier_scale", std::to_string(modifier_scale)},
    };
    return circle_pad_param.Serialize();
}
} // namespace InputCommon


code:
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

namespace Common {
class ParamPackage;
}

namespace Common::Input {
enum class ButtonNames;
}

namespace Settings::NativeAnalog {
enum Values : int;
}

namespace Settings::NativeButton {
enum Values : int;
}

namespace Settings::NativeMotion {
enum Values : int;
}

namespace InputCommon {
class Android;
class Camera;
class Keyboard;
class Mouse;
class TouchScreen;
class VirtualAmiibo;
class VirtualGamepad;
struct MappingData;
} // namespace InputCommon

namespace InputCommon::TasInput {
class Tas;
} // namespace InputCommon::TasInput

namespace InputCommon {
namespace Polling {
/// Type of input desired for mapping purposes
enum class InputType { None, Button, Stick, Motion, Touch };
} // namespace Polling

/**
 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
 * mapping for the device.
 */
using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>;

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

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

    InputSubsystem(InputSubsystem&&) = delete;
    InputSubsystem& operator=(InputSubsystem&&) = delete;

    /// Initializes and registers all built-in input device factories.
    void Initialize();

    /// Unregisters all built-in input device factories and shuts them down.
    void Shutdown();

    /// Retrieves the underlying keyboard device.
    [[nodiscard]] Keyboard* GetKeyboard();

    /// Retrieves the underlying keyboard device.
    [[nodiscard]] const Keyboard* GetKeyboard() const;

    /// Retrieves the underlying mouse device.
    [[nodiscard]] Mouse* GetMouse();

    /// Retrieves the underlying mouse device.
    [[nodiscard]] const Mouse* GetMouse() const;

    /// Retrieves the underlying touch screen device.
    [[nodiscard]] TouchScreen* GetTouchScreen();

    /// Retrieves the underlying touch screen device.
    [[nodiscard]] const TouchScreen* GetTouchScreen() const;

    /// Retrieves the underlying tas input device.
    [[nodiscard]] TasInput::Tas* GetTas();

    /// Retrieves the underlying tas input device.
    [[nodiscard]] const TasInput::Tas* GetTas() const;

    /// Retrieves the underlying camera input device.
    [[nodiscard]] Camera* GetCamera();

    /// Retrieves the underlying camera input device.
    [[nodiscard]] const Camera* GetCamera() const;

    /// Retrieves the underlying android input device.
    [[nodiscard]] Android* GetAndroid();

    /// Retrieves the underlying android input device.
    [[nodiscard]] const Android* GetAndroid() const;

    /// Retrieves the underlying virtual amiibo input device.
    [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo();

    /// Retrieves the underlying virtual amiibo input device.
    [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const;

    /// Retrieves the underlying virtual gamepad input device.
    [[nodiscard]] VirtualGamepad* GetVirtualGamepad();

    /// Retrieves the underlying virtual gamepad input device.
    [[nodiscard]] const VirtualGamepad* GetVirtualGamepad() const;

    /**
     * Returns all available input devices that this Factory can create a new device with.
     * Each returned ParamPackage should have a `display` field used for display, a `engine` field
     * for backends to determine if this backend is meant to service the request and any other
     * information needed to identify this in the backend later.
     */
    [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;

    /// Retrieves the analog mappings for the given device.
    [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;

    /// Retrieves the button mappings for the given device.
    [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;

    /// Retrieves the motion mappings for the given device.
    [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;

    /// Returns an enum containing the name to be displayed from the input engine.
    [[nodiscard]] Common::Input::ButtonNames GetButtonName(
        const Common::ParamPackage& params) const;

    /// Returns true if device is a controller.
    [[nodiscard]] bool IsController(const Common::ParamPackage& params) const;

    /// Returns true if axis of a stick aren't mapped in the correct direction
    [[nodiscard]] bool IsStickInverted(const Common::ParamPackage& device) const;

    /// Reloads the input devices.
    void ReloadInputDevices();

    /// Start polling from all backends for a desired input type.
    void BeginMapping(Polling::InputType type);

    /// Returns an input event with mapping information.
    [[nodiscard]] Common::ParamPackage GetNextInput() const;

    /// Stop polling from all backends.
    void StopMapping() const;

    /// Signals SDL driver for new input events
    void PumpEvents() const;

private:
    struct Impl;
    std::unique_ptr<Impl> impl;
};

/// Generates a serialized param package for creating a keyboard button device.
std::string GenerateKeyboardParam(int key_code);

/// Generates a serialized param package for creating an analog device taking input from keyboard.
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
                                        int key_modifier, float modifier_scale);
} // namespace InputCommon


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

#pragma once

#include "common/common_precompiled_headers.h"


code:
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later

add_library(network STATIC
    announce_multiplayer_session.cpp
    announce_multiplayer_session.h
    network.cpp
    network.h
    packet.cpp
    packet.h
    precompiled_headers.h
    room.cpp
    room.h
    room_member.cpp
    room_member.h
    verify_user.cpp
    verify_user.h
)

create_target_directory_groups(network)

target_link_libraries(network PRIVATE common enet::enet Boost::headers)
if (ENABLE_WEB_SERVICE)
    target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE)
    target_link_libraries(network PRIVATE web_service)
endif()

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


code:
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <chrono>
#include <future>
#include <vector>
#include "announce_multiplayer_session.h"
#include "common/announce_multiplayer_room.h"
#include "common/assert.h"
#include "common/settings.h"
#include "network/network.h"

#ifdef ENABLE_WEB_SERVICE
#include "web_service/announce_room_json.h"
#endif

namespace Core {

// Time between room is announced to web_service
static constexpr std::chrono::seconds announce_time_interval(15);

AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_)
    : room_network{room_network_} {
#ifdef ENABLE_WEB_SERVICE
    backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
                                                     Settings::values.yuzu_username.GetValue(),
                                                     Settings::values.yuzu_token.GetValue());
#else
    backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>();
#endif
}

WebService::WebResult AnnounceMultiplayerSession::Register() {
    auto room = room_network.GetRoom().lock();
    if (!room) {
        return WebService::WebResult{WebService::WebResult::Code::LibError,
                                     "Network is not initialized", ""};
    }
    if (room->GetState() != Network::Room::State::Open) {
        return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""};
    }
    UpdateBackendData(room);
    WebService::WebResult result = backend->Register();
    if (result.result_code != WebService::WebResult::Code::Success) {
        return result;
    }
    LOG_INFO(WebService, "Room has been registered");
    room->SetVerifyUID(result.returned_data);
    registered = true;
    return WebService::WebResult{WebService::WebResult::Code::Success, "", ""};
}

void AnnounceMultiplayerSession::Start() {
    if (announce_multiplayer_thread) {
        Stop();
    }
    shutdown_event.Reset();
    announce_multiplayer_thread =
        std::make_unique<std::thread>(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this);
}

void AnnounceMultiplayerSession::Stop() {
    if (announce_multiplayer_thread) {
        shutdown_event.Set();
        announce_multiplayer_thread->join();
        announce_multiplayer_thread.reset();
        backend->Delete();
        registered = false;
    }
}

AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback(
    std::function<void(const WebService::WebResult&)> function) {
    std::lock_guard lock(callback_mutex);
    auto handle = std::make_shared<std::function<void(const WebService::WebResult&)>>(function);
    error_callbacks.insert(handle);
    return handle;
}

void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) {
    std::lock_guard lock(callback_mutex);
    error_callbacks.erase(handle);
}

AnnounceMultiplayerSession::~AnnounceMultiplayerSession() {
    Stop();
}

void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room> room) {
    Network::RoomInformation room_information = room->GetRoomInformation();
    std::vector<AnnounceMultiplayerRoom::Member> memberlist = room->GetRoomMemberList();
    backend->SetRoomInformation(room_information.name, room_information.description,
                                room_information.port, room_information.member_slots,
                                Network::network_version, room->HasPassword(),
                                room_information.preferred_game);
    backend->ClearPlayers();
    for (const auto& member : memberlist) {
        backend->AddPlayer(member);
    }
}

void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
    // Invokes all current bound error callbacks.
    const auto ErrorCallback = [this](WebService::WebResult result) {
        std::lock_guard lock(callback_mutex);
        for (auto callback : error_callbacks) {
            (*callback)(result);
        }
    };

    if (!registered) {
        WebService::WebResult result = Register();
        if (result.result_code != WebService::WebResult::Code::Success) {
            ErrorCallback(result);
            return;
        }
    }

    auto update_time = std::chrono::steady_clock::now();
    std::future<WebService::WebResult> future;
    while (!shutdown_event.WaitUntil(update_time)) {
        update_time += announce_time_interval;
        auto room = room_network.GetRoom().lock();
        if (!room) {
            break;
        }
        if (room->GetState() != Network::Room::State::Open) {
            break;
        }
        UpdateBackendData(room);
        WebService::WebResult result = backend->Update();
        if (result.result_code != WebService::WebResult::Code::Success) {
            ErrorCallback(result);
        }
        if (result.result_string == "404") {
            registered = false;
            // Needs to register the room again
            WebService::WebResult register_result = Register();
            if (register_result.result_code != WebService::WebResult::Code::Success) {
                ErrorCallback(register_result);
            }
        }
    }
}

AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() {
    return backend->GetRoomList();
}

bool AnnounceMultiplayerSession::IsRunning() const {
    return announce_multiplayer_thread != nullptr;
}

void AnnounceMultiplayerSession::UpdateCredentials() {
    ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running");

#ifdef ENABLE_WEB_SERVICE
    backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
                                                     Settings::values.yuzu_username.GetValue(),
                                                     Settings::values.yuzu_token.GetValue());
#endif
}

} // namespace Core


code:
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "common/thread.h"

namespace Network {
class Room;
class RoomNetwork;
} // namespace Network

namespace Core {

/**
 * Instruments AnnounceMultiplayerRoom::Backend.
 * Creates a thread that regularly updates the room information and submits them
 * An async get of room information is also possible
 */
class AnnounceMultiplayerSession {
public:
    using CallbackHandle = std::shared_ptr<std::function<void(const WebService::WebResult&)>>;
    AnnounceMultiplayerSession(Network::RoomNetwork& room_network_);
    ~AnnounceMultiplayerSession();

    /**
     * Allows to bind a function that will get called if the announce encounters an error
     * @param function The function that gets called
     * @return A handle that can be used the unbind the function
     */
    CallbackHandle BindErrorCallback(std::function<void(const WebService::WebResult&)> function);

    /**
     * Unbind a function from the error callbacks
     * @param handle The handle for the function that should get unbind
     */
    void UnbindErrorCallback(CallbackHandle handle);

    /**
     * Registers a room to web services
     * @return The result of the registration attempt.
     */
    WebService::WebResult Register();

    /**
     * Starts the announce of a room to web services
     */
    void Start();

    /**
     * Stops the announce to web services
     */
    void Stop();

    /**
     *  Returns a list of all room information the backend got
     * @param func A function that gets executed when the async get finished, e.g. a signal
     * @return a list of rooms received from the web service
     */
    AnnounceMultiplayerRoom::RoomList GetRoomList();

    /**
     * Whether the announce session is still running
     */
    bool IsRunning() const;

    /**
     * Recreates the backend, updating the credentials.
     * This can only be used when the announce session is not running.
     */
    void UpdateCredentials();

private:
    void UpdateBackendData(std::shared_ptr<Network::Room> room);
    void AnnounceMultiplayerLoop();

    Common::Event shutdown_event;
    std::mutex callback_mutex;
    std::set<CallbackHandle> error_callbacks;
    std::unique_ptr<std::thread> announce_multiplayer_thread;

    /// Backend interface that logs fields
    std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend;

    std::atomic_bool registered = false; ///< Whether the room has been registered

    Network::RoomNetwork& room_network;
};

} // namespace Core


code:
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "common/logging/log.h"
#include "enet/enet.h"
#include "network/network.h"

namespace Network {

RoomNetwork::RoomNetwork() {
    m_room = std::make_shared<Room>();
    m_room_member = std::make_shared<RoomMember>();
}

bool RoomNetwork::Init() {
    if (enet_initialize() != 0) {
        LOG_ERROR(Network, "Error initializing ENet");
        return false;
    }
    m_room = std::make_shared<Room>();
    m_room_member = std::make_shared<RoomMember>();
    LOG_DEBUG(Network, "initialized OK");
    return true;
}

std::weak_ptr<Room> RoomNetwork::GetRoom() {
    return m_room;
}

std::weak_ptr<RoomMember> RoomNetwork::GetRoomMember() {
    return m_room_member;
}

void RoomNetwork::Shutdown() {
    if (m_room_member) {
        if (m_room_member->IsConnected())
            m_room_member->Leave();
        m_room_member.reset();
    }
    if (m_room) {
        if (m_room->GetState() == Room::State::Open)
            m_room->Destroy();
        m_room.reset();
    }
    enet_deinitialize();
    LOG_DEBUG(Network, "shutdown OK");
}

} // namespace Network


code:
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include "network/room.h"
#include "network/room_member.h"

namespace Network {

class RoomNetwork {
public:
    RoomNetwork();

    /// Initializes and registers the network device, the room, and the room member.
    bool Init();

    /// Returns a pointer to the room handle
    std::weak_ptr<Room> GetRoom();

    /// Returns a pointer to the room member handle
    std::weak_ptr<RoomMember> GetRoomMember();

    /// Unregisters the network device, the room, and the room member and shut them down.
    void Shutdown();

private:
    std::shared_ptr<RoomMember> m_room_member; ///< RoomMember (Client) for network games
    std::shared_ptr<Room> m_room;              ///< Room (Server) for network games
};

} // namespace Network


code:
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
#include <cstring>
#include <string>
#include "network/packet.h"

namespace Network {

#ifndef htonll
static u64 htonll(u64 x) {
    return ((1 == htonl(1)) ? (x) : ((uint64_t)htonl((x)&0xFFFFFFFF) << 32) | htonl((x) >> 32));
}
#endif

#ifndef ntohll
static u64 ntohll(u64 x) {
    return ((1 == ntohl(1)) ? (x) : ((uint64_t)ntohl((x)&0xFFFFFFFF) << 32) | ntohl((x) >> 32));
}
#endif

void Packet::Append(const void* in_data, std::size_t size_in_bytes) {
    if (in_data && (size_in_bytes > 0)) {
        std::size_t start = data.size();
        data.resize(start + size_in_bytes);
        std::memcpy(&data[start], in_data, size_in_bytes);
    }
}

void Packet::Read(void* out_data, std::size_t size_in_bytes) {
    if (out_data && CheckSize(size_in_bytes)) {
        std::memcpy(out_data, &data[read_pos], size_in_bytes);
        read_pos += size_in_bytes;
    }
}

void Packet::Clear() {
    data.clear();
    read_pos = 0;
    is_valid = true;
}

const void* Packet::GetData() const {
    return !data.empty() ? &data[0] : nullptr;
}

void Packet::IgnoreBytes(u32 length) {
    read_pos += length;
}

std::size_t Packet::GetDataSize() const {
    return data.size();
}

bool Packet::EndOfPacket() const {
    return read_pos >= data.size();
}

Packet::operator bool() const {
    return is_valid;
}

Packet& Packet::Read(bool& out_data) {
    u8 value{};
    if (Read(value)) {
        out_data = (value != 0);
    }
    return *this;
}

Packet& Packet::Read(s8& out_data) {
    Read(&out_data, sizeof(out_data));
    return *this;
}

Packet& Packet::Read(u8& out_data) {
    Read(&out_data, sizeof(out_data));
    return *this;
}

Packet& Packet::Read(s16& out_data) {
    s16 value{};
    Read(&value, sizeof(value));
    out_data = ntohs(value);
    return *this;
}

Packet& Packet::Read(u16& out_data) {
    u16 value{};
    Read(&value, sizeof(value));
    out_data = ntohs(value);
    return *this;
}

Packet& Packet::Read(s32& out_data) {
    s32 value{};
    Read(&value, sizeof(value));
    out_data = ntohl(value);
    return *this;
}

Packet& Packet::Read(u32& out_data) {
    u32 value{};
    Read(&value, sizeof(value));
    out_data = ntohl(value);
    return *this;
}

Packet& Packet::Read(s64& out_data) {
    s64 value{};
    Read(&value, sizeof(value));
    out_data = ntohll(value);
    return *this;
}

Packet& Packet::Read(u64& out_data) {
    u64 value{};
    Read(&value, sizeof(value));
    out_data = ntohll(value);
    return *this;
}

Packet& Packet::Read(float& out_data) {
    Read(&out_data, sizeof(out_data));
    return *this;
}

Packet& Packet::Read(double& out_data) {
    Read(&out_data, sizeof(out_data));
    return *this;
}

Packet& Packet::Read(char* out_data) {
    // First extract string length
    u32 length = 0;
    Read(length);

    if ((length > 0) && CheckSize(length)) {
        // Then extract characters
        std::memcpy(out_data, &data[read_pos], length);
        out_data[length] = '\0';

        // Update reading position
        read_pos += length;
    }

    return *this;
}

Packet& Packet::Read(std::string& out_data) {
    // First extract string length
    u32 length = 0;
    Read(length);

    out_data.clear();
    if ((length > 0) && CheckSize(length)) {
        // Then extract characters
        out_data.assign(&data[read_pos], length);

        // Update reading position
        read_pos += length;
    }

    return *this;
}

Packet& Packet::Write(bool in_data) {
    Write(static_cast<u8>(in_data));
    return *this;
}

Packet& Packet::Write(s8 in_data) {
    Append(&in_data, sizeof(in_data));
    return *this;
}

Packet& Packet::Write(u8 in_data) {
    Append(&in_data, sizeof(in_data));
    return *this;
}

Packet& Packet::Write(s16 in_data) {
    s16 toWrite = htons(in_data);
    Append(&toWrite, sizeof(toWrite));
    return *this;
}

Packet& Packet::Write(u16 in_data) {
    u16 toWrite = htons(in_data);
    Append(&toWrite, sizeof(toWrite));
    return *this;
}

Packet& Packet::Write(s32 in_data) {
    s32 toWrite = htonl(in_data);
    Append(&toWrite, sizeof(toWrite));
    return *this;
}

Packet& Packet::Write(u32 in_data) {
    u32 toWrite = htonl(in_data);
    Append(&toWrite, sizeof(toWrite));
    return *this;
}

Packet& Packet::Write(s64 in_data) {
    s64 toWrite = htonll(in_data);
    Append(&toWrite, sizeof(toWrite));
    return *this;
}

Packet& Packet::Write(u64 in_data) {
    u64 toWrite = htonll(in_data);
    Append(&toWrite, sizeof(toWrite));
    return *this;
}

Packet& Packet::Write(float in_data) {
    Append(&in_data, sizeof(in_data));
    return *this;
}

Packet& Packet::Write(double in_data) {
    Append(&in_data, sizeof(in_data));
    return *this;
}

Packet& Packet::Write(const char* in_data) {
    // First insert string length
    u32 length = static_cast<u32>(std::strlen(in_data));
    Write(length);

    // Then insert characters
    Append(in_data, length * sizeof(char));

    return *this;
}

Packet& Packet::Write(const std::string& in_data) {
    // First insert string length
    u32 length = static_cast<u32>(in_data.size());
    Write(length);

    // Then insert characters
    if (length > 0)
        Append(in_data.c_str(), length * sizeof(std::string::value_type));

    return *this;
}

bool Packet::CheckSize(std::size_t size) {
    is_valid = is_valid && (read_pos + size <= data.size());

    return is_valid;
}

} // namespace Network