| | |
| | |
| | |
| |
|
| | #include <atomic> |
| | #include <list> |
| | #include <mutex> |
| | #include <utility> |
| | #include "common/assert.h" |
| | #include "common/threadsafe_queue.h" |
| | #include "input_common/gcadapter/gc_adapter.h" |
| | #include "input_common/gcadapter/gc_poller.h" |
| |
|
| | namespace InputCommon { |
| | namespace { |
| | constexpr std::array<GCAdapter::PadButton, Settings::NativeButton::NumButtons> gc_to_3ds_mapping{{ |
| | GCAdapter::PadButton::ButtonA, |
| | GCAdapter::PadButton::ButtonB, |
| | GCAdapter::PadButton::ButtonX, |
| | GCAdapter::PadButton::ButtonY, |
| | GCAdapter::PadButton::ButtonUp, |
| | GCAdapter::PadButton::ButtonDown, |
| | GCAdapter::PadButton::ButtonLeft, |
| | GCAdapter::PadButton::ButtonRight, |
| | GCAdapter::PadButton::TriggerL, |
| | GCAdapter::PadButton::TriggerR, |
| | GCAdapter::PadButton::ButtonStart, |
| | GCAdapter::PadButton::TriggerZ, |
| | GCAdapter::PadButton::Undefined, |
| | GCAdapter::PadButton::Undefined, |
| | GCAdapter::PadButton::Undefined, |
| | GCAdapter::PadButton::Undefined, |
| | GCAdapter::PadButton::Undefined, |
| | }}; |
| | } |
| | class GCButton final : public Input::ButtonDevice { |
| | public: |
| | explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter) |
| | : port(port_), button(button_), gcadapter(adapter) {} |
| |
|
| | ~GCButton() override; |
| |
|
| | bool GetStatus() const override { |
| | if (gcadapter->DeviceConnected(port)) { |
| | return (gcadapter->GetPadState(port).buttons & button) != 0; |
| | } |
| | return false; |
| | } |
| |
|
| | private: |
| | const int port; |
| | const int button; |
| | GCAdapter::Adapter* gcadapter; |
| | }; |
| |
|
| | class GCAxisButton final : public Input::ButtonDevice { |
| | public: |
| | explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_, |
| | GCAdapter::Adapter* adapter) |
| | : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), |
| | gcadapter(adapter) {} |
| |
|
| | bool GetStatus() const override { |
| | if (gcadapter->DeviceConnected(port)) { |
| | const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis); |
| | const float axis_value = current_axis_value / 128.0f; |
| | if (trigger_if_greater) { |
| | return axis_value > threshold; |
| | } |
| | return axis_value < -threshold; |
| | } |
| | return false; |
| | } |
| |
|
| | private: |
| | const u32 port; |
| | const u32 axis; |
| | float threshold; |
| | bool trigger_if_greater; |
| | const GCAdapter::Adapter* gcadapter; |
| | }; |
| |
|
| | GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) |
| | : adapter(std::move(adapter_)) {} |
| |
|
| | GCButton::~GCButton() = default; |
| |
|
| | std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) { |
| | const int button_id = params.Get("button", 0); |
| | const int port = params.Get("port", 0); |
| |
|
| | constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick); |
| |
|
| | |
| | if (button_id != PAD_STICK_ID) { |
| | return std::make_unique<GCButton>(port, button_id, adapter.get()); |
| | } |
| |
|
| | |
| | if (button_id == PAD_STICK_ID) { |
| | const int axis = params.Get("axis", 0); |
| | const float threshold = params.Get("threshold", 0.25f); |
| | const std::string direction_name = params.Get("direction", ""); |
| | bool trigger_if_greater; |
| | if (direction_name == "+") { |
| | trigger_if_greater = true; |
| | } else if (direction_name == "-") { |
| | trigger_if_greater = false; |
| | } else { |
| | trigger_if_greater = true; |
| | LOG_ERROR(Input, "Unknown direction {}", direction_name); |
| | } |
| | return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater, |
| | adapter.get()); |
| | } |
| |
|
| | UNREACHABLE(); |
| | return nullptr; |
| | } |
| |
|
| | Common::ParamPackage GCButtonFactory::GetNextInput() { |
| | Common::ParamPackage params; |
| | GCAdapter::GCPadStatus pad; |
| | auto& queue = adapter->GetPadQueue(); |
| | while (queue.Pop(pad)) { |
| | |
| | params.Set("engine", "gcpad"); |
| | params.Set("port", static_cast<s32>(pad.port)); |
| | if (pad.button != GCAdapter::PadButton::Undefined) { |
| | params.Set("button", static_cast<u16>(pad.button)); |
| | } |
| |
|
| | |
| | if (pad.axis != GCAdapter::PadAxes::Undefined) { |
| | params.Set("axis", static_cast<u8>(pad.axis)); |
| | params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick)); |
| | params.Set("threshold", "0.25"); |
| | if (pad.axis_value > 0) { |
| | params.Set("direction", "+"); |
| | } else { |
| | params.Set("direction", "-"); |
| | } |
| | break; |
| | } |
| | } |
| | return params; |
| | } |
| |
|
| | Common::ParamPackage GCButtonFactory::GetGcTo3DSMappedButton( |
| | int port, Settings::NativeButton::Values button) { |
| | Common::ParamPackage params({{"engine", "gcpad"}}); |
| | params.Set("port", port); |
| | auto mapped_button = gc_to_3ds_mapping[static_cast<int>(button)]; |
| | if (mapped_button != GCAdapter::PadButton::Undefined) { |
| | params.Set("button", static_cast<u16>(mapped_button)); |
| | } |
| | return params; |
| | } |
| |
|
| | void GCButtonFactory::Start() { |
| | polling = true; |
| | adapter->BeginConfiguration(); |
| | } |
| |
|
| | void GCButtonFactory::Stop() { |
| | polling = false; |
| | adapter->EndConfiguration(); |
| | } |
| |
|
| | class GCAnalog final : public Input::AnalogDevice { |
| | public: |
| | explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, |
| | const GCAdapter::Adapter* adapter) |
| | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {} |
| |
|
| | float GetAxis(u32 axis) const { |
| | if (gcadapter->DeviceConnected(port)) { |
| | std::lock_guard lock{mutex}; |
| | const auto axis_value = |
| | static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis)); |
| | return (axis_value) / 50.0f; |
| | } |
| | return 0.0f; |
| | } |
| |
|
| | std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { |
| | float x = GetAxis(analog_axis_x); |
| | float y = GetAxis(analog_axis_y); |
| | |
| | |
| | float r = x * x + y * y; |
| | if (r > 1.0f) { |
| | r = std::sqrt(r); |
| | x /= r; |
| | y /= r; |
| | } |
| |
|
| | return {x, y}; |
| | } |
| |
|
| | std::tuple<float, float> GetStatus() const override { |
| | const auto [x, y] = GetAnalog(axis_x, axis_y); |
| | const float r = std::sqrt((x * x) + (y * y)); |
| | if (r > deadzone) { |
| | return {x / r * (r - deadzone) / (1 - deadzone), |
| | y / r * (r - deadzone) / (1 - deadzone)}; |
| | } |
| | return {0.0f, 0.0f}; |
| | } |
| |
|
| | private: |
| | const u32 port; |
| | const u32 axis_x; |
| | const u32 axis_y; |
| | const float deadzone; |
| | const GCAdapter::Adapter* gcadapter; |
| | mutable std::mutex mutex; |
| | }; |
| |
|
| | |
| | GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) |
| | : adapter(std::move(adapter_)) {} |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) { |
| | const auto port = static_cast<u32>(params.Get("port", 0)); |
| | const auto axis_x = static_cast<u32>(params.Get("axis_x", 0)); |
| | const auto axis_y = static_cast<u32>(params.Get("axis_y", 1)); |
| | const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); |
| |
|
| | return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get()); |
| | } |
| |
|
| | void GCAnalogFactory::Start() { |
| | polling = true; |
| | adapter->BeginConfiguration(); |
| | } |
| |
|
| | void GCAnalogFactory::Stop() { |
| | polling = false; |
| | adapter->EndConfiguration(); |
| | } |
| |
|
| | Common::ParamPackage GCAnalogFactory::GetNextInput() { |
| | GCAdapter::GCPadStatus pad; |
| | Common::ParamPackage params; |
| | auto& queue = adapter->GetPadQueue(); |
| | while (queue.Pop(pad)) { |
| | if (pad.button != GCAdapter::PadButton::Undefined) { |
| | params.Set("engine", "gcpad"); |
| | params.Set("port", static_cast<s32>(pad.port)); |
| | params.Set("button", static_cast<u16>(pad.button)); |
| | return params; |
| | } |
| | if (pad.axis == GCAdapter::PadAxes::Undefined || |
| | std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) { |
| | continue; |
| | } |
| | |
| | |
| | const u8 axis = static_cast<u8>(pad.axis); |
| | if (axis == 0 || axis == 1) { |
| | analog_x_axis = 0; |
| | analog_y_axis = 1; |
| | controller_number = static_cast<s32>(pad.port); |
| | break; |
| | } |
| | if (axis == 2 || axis == 3) { |
| | analog_x_axis = 2; |
| | analog_y_axis = 3; |
| | controller_number = static_cast<s32>(pad.port); |
| | break; |
| | } |
| |
|
| | if (analog_x_axis == -1) { |
| | analog_x_axis = axis; |
| | controller_number = static_cast<s32>(pad.port); |
| | } else if (analog_y_axis == -1 && analog_x_axis != axis && |
| | controller_number == static_cast<s32>(pad.port)) { |
| | analog_y_axis = axis; |
| | break; |
| | } |
| | } |
| | if (analog_x_axis != -1 && analog_y_axis != -1) { |
| | params.Set("engine", "gcpad"); |
| | params.Set("port", controller_number); |
| | params.Set("axis_x", analog_x_axis); |
| | params.Set("axis_y", analog_y_axis); |
| | analog_x_axis = -1; |
| | analog_y_axis = -1; |
| | controller_number = -1; |
| | return params; |
| | } |
| | return params; |
| | } |
| |
|
| | Common::ParamPackage GCAnalogFactory::GetGcTo3DSMappedAnalog( |
| | int port, Settings::NativeAnalog::Values analog) { |
| | int x_axis, y_axis; |
| | Common::ParamPackage params({{"engine", "gcpad"}}); |
| | params.Set("port", port); |
| | if (analog == Settings::NativeAnalog::Values::CirclePad) { |
| | x_axis = static_cast<s32>(GCAdapter::PadAxes::StickX); |
| | y_axis = static_cast<s32>(GCAdapter::PadAxes::StickY); |
| | } else if (analog == Settings::NativeAnalog::Values::CStick) { |
| | x_axis = static_cast<s32>(GCAdapter::PadAxes::SubstickX); |
| | y_axis = static_cast<s32>(GCAdapter::PadAxes::SubstickY); |
| | } else { |
| | LOG_WARNING(Input, "analog value out of range {}", analog); |
| | return {{}}; |
| | } |
| | params.Set("axis_x", x_axis); |
| | params.Set("axis_y", y_axis); |
| | return params; |
| | } |
| |
|
| | } |
| |
|