diff options
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 82 | ||||
| -rw-r--r-- | src/core/hid/emulated_controller.h | 9 | ||||
| -rw-r--r-- | src/input_common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/input_common/drivers/virtual_gamepad.cpp | 78 | ||||
| -rw-r--r-- | src/input_common/drivers/virtual_gamepad.h | 73 | ||||
| -rw-r--r-- | src/input_common/main.cpp | 23 | ||||
| -rw-r--r-- | src/input_common/main.h | 7 | 
7 files changed, 274 insertions, 0 deletions
| diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 67969e938..f238d6ccd 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -145,6 +145,7 @@ void EmulatedController::LoadDevices() {      output_params[3].Set("output", true);      LoadTASParams(); +    LoadVirtualGamepadParams();      std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);      std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); @@ -163,6 +164,12 @@ void EmulatedController::LoadDevices() {                             Common::Input::CreateInputDevice);      std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),                             Common::Input::CreateInputDevice); + +    // Initialize virtual gamepad devices +    std::ranges::transform(virtual_button_params, virtual_button_devices.begin(), +                           Common::Input::CreateInputDevice); +    std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(), +                           Common::Input::CreateInputDevice);  }  void EmulatedController::LoadTASParams() { @@ -205,6 +212,46 @@ void EmulatedController::LoadTASParams() {      tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);  } +void EmulatedController::LoadVirtualGamepadParams() { +    const auto player_index = NpadIdTypeToIndex(npad_id_type); +    Common::ParamPackage common_params{}; +    common_params.Set("engine", "virtual_gamepad"); +    common_params.Set("port", static_cast<int>(player_index)); +    for (auto& param : virtual_button_params) { +        param = common_params; +    } +    for (auto& param : virtual_stick_params) { +        param = common_params; +    } + +    // TODO(german77): Replace this with an input profile or something better +    virtual_button_params[Settings::NativeButton::A].Set("button", 0); +    virtual_button_params[Settings::NativeButton::B].Set("button", 1); +    virtual_button_params[Settings::NativeButton::X].Set("button", 2); +    virtual_button_params[Settings::NativeButton::Y].Set("button", 3); +    virtual_button_params[Settings::NativeButton::LStick].Set("button", 4); +    virtual_button_params[Settings::NativeButton::RStick].Set("button", 5); +    virtual_button_params[Settings::NativeButton::L].Set("button", 6); +    virtual_button_params[Settings::NativeButton::R].Set("button", 7); +    virtual_button_params[Settings::NativeButton::ZL].Set("button", 8); +    virtual_button_params[Settings::NativeButton::ZR].Set("button", 9); +    virtual_button_params[Settings::NativeButton::Plus].Set("button", 10); +    virtual_button_params[Settings::NativeButton::Minus].Set("button", 11); +    virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12); +    virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); +    virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); +    virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); +    virtual_button_params[Settings::NativeButton::SL].Set("button", 16); +    virtual_button_params[Settings::NativeButton::SR].Set("button", 17); +    virtual_button_params[Settings::NativeButton::Home].Set("button", 18); +    virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); + +    virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); +    virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); +    virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); +    virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); +} +  void EmulatedController::ReloadInput() {      // If you load any device here add the equivalent to the UnloadInput() function      LoadDevices(); @@ -322,6 +369,35 @@ void EmulatedController::ReloadInput() {                  },          });      } + +    // Use a common UUID for Virtual Gamepad +    static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{ +        {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; + +    // Register virtual devices. No need to force update +    for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) { +        if (!virtual_button_devices[index]) { +            continue; +        } +        virtual_button_devices[index]->SetCallback({ +            .on_change = +                [this, index](const Common::Input::CallbackStatus& callback) { +                    SetButton(callback, index, VIRTUAL_UUID); +                }, +        }); +    } + +    for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) { +        if (!virtual_stick_devices[index]) { +            continue; +        } +        virtual_stick_devices[index]->SetCallback({ +            .on_change = +                [this, index](const Common::Input::CallbackStatus& callback) { +                    SetStick(callback, index, VIRTUAL_UUID); +                }, +        }); +    }  }  void EmulatedController::UnloadInput() { @@ -349,6 +425,12 @@ void EmulatedController::UnloadInput() {      for (auto& stick : tas_stick_devices) {          stick.reset();      } +    for (auto& button : virtual_button_devices) { +        button.reset(); +    } +    for (auto& stick : virtual_stick_devices) { +        stick.reset(); +    }      camera_devices.reset();      nfc_devices.reset();  } diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index fa7a34278..a398543a6 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -385,6 +385,9 @@ private:      /// Set the params for TAS devices      void LoadTASParams(); +    /// Set the params for virtual pad devices +    void LoadVirtualGamepadParams(); +      /**       * @param use_temporary_value If true tmp_npad_type will be used       * @return true if the controller style is fullkey @@ -500,6 +503,12 @@ private:      ButtonDevices tas_button_devices;      StickDevices tas_stick_devices; +    // Virtual gamepad related variables +    ButtonParams virtual_button_params; +    StickParams virtual_stick_params; +    ButtonDevices virtual_button_devices; +    StickDevices virtual_stick_devices; +      mutable std::mutex mutex;      mutable std::mutex callback_mutex;      std::unordered_map<int, ControllerUpdateCallback> callback_list; diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 7932aaab0..f24c89b04 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -20,6 +20,8 @@ add_library(input_common STATIC      drivers/udp_client.h      drivers/virtual_amiibo.cpp      drivers/virtual_amiibo.h +    drivers/virtual_gamepad.cpp +    drivers/virtual_gamepad.h      helpers/stick_from_buttons.cpp      helpers/stick_from_buttons.h      helpers/touch_from_buttons.cpp diff --git a/src/input_common/drivers/virtual_gamepad.cpp b/src/input_common/drivers/virtual_gamepad.cpp new file mode 100644 index 000000000..7db945aa6 --- /dev/null +++ b/src/input_common/drivers/virtual_gamepad.cpp @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "input_common/drivers/virtual_gamepad.h" + +namespace InputCommon { +constexpr std::size_t PlayerIndexCount = 10; + +VirtualGamepad::VirtualGamepad(std::string input_engine_) : InputEngine(std::move(input_engine_)) { +    for (std::size_t i = 0; i < PlayerIndexCount; i++) { +        PreSetController(GetIdentifier(i)); +    } +} + +void VirtualGamepad::SetButtonState(std::size_t player_index, int button_id, bool value) { +    if (player_index > PlayerIndexCount) { +        return; +    } +    const auto identifier = GetIdentifier(player_index); +    SetButton(identifier, button_id, value); +} + +void VirtualGamepad::SetButtonState(std::size_t player_index, VirtualButton button_id, bool value) { +    SetButtonState(player_index, static_cast<int>(button_id), value); +} + +void VirtualGamepad::SetStickPosition(std::size_t player_index, int axis_id, float x_value, +                                      float y_value) { +    if (player_index > PlayerIndexCount) { +        return; +    } +    const auto identifier = GetIdentifier(player_index); +    SetAxis(identifier, axis_id * 2, x_value); +    SetAxis(identifier, (axis_id * 2) + 1, y_value); +} + +void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, +                                      float y_value) { +    SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value); +} + +void VirtualGamepad::ResetControllers() { +    for (std::size_t i = 0; i < PlayerIndexCount; i++) { +        SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f); +        SetStickPosition(i, VirtualStick::Right, 0.0f, 0.0f); + +        SetButtonState(i, VirtualButton::ButtonA, false); +        SetButtonState(i, VirtualButton::ButtonB, false); +        SetButtonState(i, VirtualButton::ButtonX, false); +        SetButtonState(i, VirtualButton::ButtonY, false); +        SetButtonState(i, VirtualButton::StickL, false); +        SetButtonState(i, VirtualButton::StickR, false); +        SetButtonState(i, VirtualButton::TriggerL, false); +        SetButtonState(i, VirtualButton::TriggerR, false); +        SetButtonState(i, VirtualButton::TriggerZL, false); +        SetButtonState(i, VirtualButton::TriggerZR, false); +        SetButtonState(i, VirtualButton::ButtonPlus, false); +        SetButtonState(i, VirtualButton::ButtonMinus, false); +        SetButtonState(i, VirtualButton::ButtonLeft, false); +        SetButtonState(i, VirtualButton::ButtonUp, false); +        SetButtonState(i, VirtualButton::ButtonRight, false); +        SetButtonState(i, VirtualButton::ButtonDown, false); +        SetButtonState(i, VirtualButton::ButtonSL, false); +        SetButtonState(i, VirtualButton::ButtonSR, false); +        SetButtonState(i, VirtualButton::ButtonHome, false); +        SetButtonState(i, VirtualButton::ButtonCapture, false); +    } +} + +PadIdentifier VirtualGamepad::GetIdentifier(std::size_t player_index) const { +    return { +        .guid = Common::UUID{}, +        .port = player_index, +        .pad = 0, +    }; +} + +} // namespace InputCommon diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h new file mode 100644 index 000000000..3df91cc6f --- /dev/null +++ b/src/input_common/drivers/virtual_gamepad.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "input_common/input_engine.h" + +namespace InputCommon { + +/** + * A virtual controller that is always assigned to the game input + */ +class VirtualGamepad final : public InputEngine { +public: +    enum class VirtualButton { +        ButtonA, +        ButtonB, +        ButtonX, +        ButtonY, +        StickL, +        StickR, +        TriggerL, +        TriggerR, +        TriggerZL, +        TriggerZR, +        ButtonPlus, +        ButtonMinus, +        ButtonLeft, +        ButtonUp, +        ButtonRight, +        ButtonDown, +        ButtonSL, +        ButtonSR, +        ButtonHome, +        ButtonCapture, +    }; + +    enum class VirtualStick { +        Left = 0, +        Right = 1, +    }; + +    explicit VirtualGamepad(std::string input_engine_); + +    /** +     * Sets the status of all buttons bound with the key to pressed +     * @param player_index the player number that will take this action +     * @param button_id the id of the button +     * @param value indicates if the button is pressed or not +     */ +    void SetButtonState(std::size_t player_index, int button_id, bool value); +    void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value); + +    /** +     * Sets the status of all buttons bound with the key to released +     * @param player_index the player number that will take this action +     * @param axis_id the id of the axis to move +     * @param x_value the position of the stick in the x axis +     * @param y_value the position of the stick in the y axis +     */ +    void SetStickPosition(std::size_t player_index, int axis_id, float x_value, float y_value); +    void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, +                          float y_value); + +    /// Restores all inputs into the neutral position +    void ResetControllers(); + +private: +    /// Returns the correct identifier corresponding to the player index +    PadIdentifier GetIdentifier(std::size_t player_index) const; +}; + +} // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 942a13535..75b856c95 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -12,6 +12,7 @@  #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" @@ -85,6 +86,12 @@ struct InputSubsystem::Impl {          Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(),                                               virtual_amiibo_output_factory); +        virtual_gamepad = std::make_shared<VirtualGamepad>("virtual_gamepad"); +        virtual_gamepad->SetMappingCallback(mapping_callback); +        virtual_gamepad_input_factory = std::make_shared<InputFactory>(virtual_gamepad); +        Common::Input::RegisterInputFactory(virtual_gamepad->GetEngineName(), +                                            virtual_gamepad_input_factory); +  #ifdef HAVE_SDL2          sdl = std::make_shared<SDLDriver>("sdl");          sdl->SetMappingCallback(mapping_callback); @@ -132,6 +139,9 @@ struct InputSubsystem::Impl {          Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName());          virtual_amiibo.reset(); +        Common::Input::UnregisterInputFactory(virtual_gamepad->GetEngineName()); +        virtual_gamepad.reset(); +  #ifdef HAVE_SDL2          Common::Input::UnregisterInputFactory(sdl->GetEngineName());          Common::Input::UnregisterOutputFactory(sdl->GetEngineName()); @@ -290,6 +300,9 @@ struct InputSubsystem::Impl {          if (engine == tas_input->GetEngineName()) {              return true;          } +        if (engine == virtual_gamepad->GetEngineName()) { +            return true; +        }  #ifdef HAVE_SDL2          if (engine == sdl->GetEngineName()) {              return true; @@ -338,6 +351,7 @@ struct InputSubsystem::Impl {      std::shared_ptr<CemuhookUDP::UDPClient> udp_client;      std::shared_ptr<Camera> camera;      std::shared_ptr<VirtualAmiibo> virtual_amiibo; +    std::shared_ptr<VirtualGamepad> virtual_gamepad;      std::shared_ptr<InputFactory> keyboard_factory;      std::shared_ptr<InputFactory> mouse_factory; @@ -347,6 +361,7 @@ struct InputSubsystem::Impl {      std::shared_ptr<InputFactory> tas_input_factory;      std::shared_ptr<InputFactory> camera_input_factory;      std::shared_ptr<InputFactory> virtual_amiibo_input_factory; +    std::shared_ptr<InputFactory> virtual_gamepad_input_factory;      std::shared_ptr<OutputFactory> keyboard_output_factory;      std::shared_ptr<OutputFactory> mouse_output_factory; @@ -423,6 +438,14 @@ 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();  } diff --git a/src/input_common/main.h b/src/input_common/main.h index 6218c37f6..1207d786c 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -34,6 +34,7 @@ class Keyboard;  class Mouse;  class TouchScreen;  class VirtualAmiibo; +class VirtualGamepad;  struct MappingData;  } // namespace InputCommon @@ -108,6 +109,12 @@ public:      /// 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 | 
