diff options
Diffstat (limited to 'src/input_common')
| -rw-r--r-- | src/input_common/gcadapter/gc_adapter.cpp | 9 | ||||
| -rw-r--r-- | src/input_common/main.cpp | 46 | ||||
| -rw-r--r-- | src/input_common/main.h | 24 | ||||
| -rw-r--r-- | src/input_common/motion_emu.cpp | 17 | ||||
| -rw-r--r-- | src/input_common/settings.cpp | 7 | ||||
| -rw-r--r-- | src/input_common/settings.h | 17 | ||||
| -rw-r--r-- | src/input_common/udp/client.cpp | 176 | ||||
| -rw-r--r-- | src/input_common/udp/client.h | 77 | ||||
| -rw-r--r-- | src/input_common/udp/udp.cpp | 181 | ||||
| -rw-r--r-- | src/input_common/udp/udp.h | 61 | 
10 files changed, 480 insertions, 135 deletions
| diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index c6c423c4b..c507c9891 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp @@ -4,7 +4,16 @@  #include <chrono>  #include <thread> + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4200) // nonstandard extension used : zero-sized array in struct/union +#endif  #include <libusb.h> +#ifdef _MSC_VER +#pragma warning(pop) +#endif +  #include "common/logging/log.h"  #include "input_common/gcadapter/gc_adapter.h" diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index ea1a1cee6..062ec66b5 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -12,6 +12,7 @@  #include "input_common/main.h"  #include "input_common/motion_emu.h"  #include "input_common/touch_from_button.h" +#include "input_common/udp/client.h"  #include "input_common/udp/udp.h"  #ifdef HAVE_SDL2  #include "input_common/sdl/sdl.h" @@ -40,7 +41,11 @@ struct InputSubsystem::Impl {          sdl = SDL::Init();  #endif -        udp = CemuhookUDP::Init(); +        udp = std::make_shared<InputCommon::CemuhookUDP::Client>(); +        udpmotion = std::make_shared<UDPMotionFactory>(udp); +        Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion); +        udptouch = std::make_shared<UDPTouchFactory>(udp); +        Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);      }      void Shutdown() { @@ -53,12 +58,17 @@ struct InputSubsystem::Impl {  #ifdef HAVE_SDL2          sdl.reset();  #endif -        udp.reset();          Input::UnregisterFactory<Input::ButtonDevice>("gcpad");          Input::UnregisterFactory<Input::AnalogDevice>("gcpad");          gcbuttons.reset();          gcanalog.reset(); + +        Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); +        Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); + +        udpmotion.reset(); +        udptouch.reset();      }      [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { @@ -109,14 +119,28 @@ struct InputSubsystem::Impl {          return {};      } +    [[nodiscard]] MotionMapping GetMotionMappingForDevice( +        const Common::ParamPackage& params) const { +        if (!params.Has("class") || params.Get("class", "") == "any") { +            return {}; +        } +        if (params.Get("class", "") == "cemuhookudp") { +            // TODO return the correct motion device +            return {}; +        } +        return {}; +    } +      std::shared_ptr<Keyboard> keyboard;      std::shared_ptr<MotionEmu> motion_emu;  #ifdef HAVE_SDL2      std::unique_ptr<SDL::State> sdl;  #endif -    std::unique_ptr<CemuhookUDP::State> udp;      std::shared_ptr<GCButtonFactory> gcbuttons;      std::shared_ptr<GCAnalogFactory> gcanalog; +    std::shared_ptr<UDPMotionFactory> udpmotion; +    std::shared_ptr<UDPTouchFactory> udptouch; +    std::shared_ptr<CemuhookUDP::Client> udp;  };  InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} @@ -175,6 +199,22 @@ const GCButtonFactory* InputSubsystem::GetGCButtons() const {      return impl->gcbuttons.get();  } +UDPMotionFactory* InputSubsystem::GetUDPMotions() { +    return impl->udpmotion.get(); +} + +const UDPMotionFactory* InputSubsystem::GetUDPMotions() const { +    return impl->udpmotion.get(); +} + +UDPTouchFactory* InputSubsystem::GetUDPTouch() { +    return impl->udptouch.get(); +} + +const UDPTouchFactory* InputSubsystem::GetUDPTouch() const { +    return impl->udptouch.get(); +} +  void InputSubsystem::ReloadInputDevices() {      if (!impl->udp) {          return; diff --git a/src/input_common/main.h b/src/input_common/main.h index f3fbf696e..dded3f1ef 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -21,10 +21,14 @@ namespace Settings::NativeButton {  enum Values : int;  } +namespace Settings::NativeMotion { +enum Values : int; +} +  namespace InputCommon {  namespace Polling { -enum class DeviceType { Button, AnalogPreferred }; +enum class DeviceType { Button, AnalogPreferred, Motion };  /**   * A class that can be used to get inputs from an input device like controllers without having to @@ -50,6 +54,8 @@ public:  class GCAnalogFactory;  class GCButtonFactory; +class UDPMotionFactory; +class UDPTouchFactory;  class Keyboard;  class MotionEmu; @@ -59,6 +65,7 @@ class MotionEmu;   */  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: @@ -103,6 +110,9 @@ public:      /// 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; +      /// Retrieves the underlying GameCube analog handler.      [[nodiscard]] GCAnalogFactory* GetGCAnalogs(); @@ -115,6 +125,18 @@ public:      /// Retrieves the underlying GameCube button handler.      [[nodiscard]] const GCButtonFactory* GetGCButtons() const; +    /// Retrieves the underlying udp motion handler. +    [[nodiscard]] UDPMotionFactory* GetUDPMotions(); + +    /// Retrieves the underlying udp motion handler. +    [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const; + +    /// Retrieves the underlying udp touch handler. +    [[nodiscard]] UDPTouchFactory* GetUDPTouch(); + +    /// Retrieves the underlying udp touch handler. +    [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const; +      /// Reloads the input devices      void ReloadInputDevices(); diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp index d4cdf76a3..69fd3c1d2 100644 --- a/src/input_common/motion_emu.cpp +++ b/src/input_common/motion_emu.cpp @@ -56,7 +56,7 @@ public:          is_tilting = false;      } -    std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() { +    Input::MotionStatus GetStatus() {          std::lock_guard guard{status_mutex};          return status;      } @@ -76,7 +76,7 @@ private:      Common::Event shutdown_event; -    std::tuple<Common::Vec3<float>, Common::Vec3<float>> status; +    Input::MotionStatus status;      std::mutex status_mutex;      // Note: always keep the thread declaration at the end so that other objects are initialized @@ -113,10 +113,19 @@ private:              gravity = QuaternionRotate(inv_q, gravity);              angular_rate = QuaternionRotate(inv_q, angular_rate); +            // TODO: Calculate the correct rotation vector and orientation matrix +            const auto matrix4x4 = q.ToMatrix(); +            const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f); +            const std::array orientation{ +                Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]), +                Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]), +                Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10]), +            }; +              // Update the sensor state              {                  std::lock_guard guard{status_mutex}; -                status = std::make_tuple(gravity, angular_rate); +                status = std::make_tuple(gravity, angular_rate, rotation, orientation);              }          }      } @@ -131,7 +140,7 @@ public:          device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);      } -    std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { +    Input::MotionStatus GetStatus() const override {          return device->GetStatus();      } diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp index 80c719cf4..b66c05856 100644 --- a/src/input_common/settings.cpp +++ b/src/input_common/settings.cpp @@ -14,6 +14,13 @@ const std::array<const char*, NumButtons> mapping = {{  }};  } +namespace NativeMotion { +const std::array<const char*, NumMotions> mapping = {{ +    "motionleft", +    "motionright", +}}; +} +  namespace NativeAnalog {  const std::array<const char*, NumAnalogs> mapping = {{      "lstick", diff --git a/src/input_common/settings.h b/src/input_common/settings.h index 2d258960b..ab0b95cf1 100644 --- a/src/input_common/settings.h +++ b/src/input_common/settings.h @@ -66,6 +66,21 @@ constexpr int NUM_STICKS_HID = NumAnalogs;  extern const std::array<const char*, NumAnalogs> mapping;  } // namespace NativeAnalog +namespace NativeMotion { +enum Values : int { +    MOTIONLEFT, +    MOTIONRIGHT, + +    NumMotions, +}; + +constexpr int MOTION_HID_BEGIN = MOTIONLEFT; +constexpr int MOTION_HID_END = NumMotions; +constexpr int NUM_MOTION_HID = NumMotions; + +extern const std::array<const char*, NumMotions> mapping; +} // namespace NativeMotion +  namespace NativeMouseButton {  enum Values {      Left, @@ -292,6 +307,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;  using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;  using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; +using MotionRaw = std::array<std::string, NativeMotion::NumMotions>;  using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;  using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;  using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; @@ -314,6 +330,7 @@ struct PlayerInput {      ControllerType controller_type;      ButtonsRaw buttons;      AnalogsRaw analogs; +    MotionRaw motions;      std::string lstick_mod;      std::string rstick_mod; diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 3f4eaf448..2b6a68d4b 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -2,14 +2,13 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. -#include <algorithm> -#include <array>  #include <chrono>  #include <cstring>  #include <functional>  #include <thread>  #include <boost/asio.hpp>  #include "common/logging/log.h" +#include "core/settings.h"  #include "input_common/udp/client.h"  #include "input_common/udp/protocol.h" @@ -131,21 +130,59 @@ static void SocketLoop(Socket* socket) {      socket->Loop();  } -Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, -               u8 pad_index, u32 client_id) -    : status(std::move(status)) { -    StartCommunication(host, port, pad_index, client_id); +Client::Client() { +    LOG_INFO(Input, "Udp Initialization started"); +    for (std::size_t client = 0; client < clients.size(); client++) { +        u8 pad = client % 4; +        StartCommunication(client, Settings::values.udp_input_address, +                           Settings::values.udp_input_port, pad, 24872); +        // Set motion parameters +        // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode +        // Real HW values are unknown, 0.0001 is an approximate to Standard +        clients[client].motion.SetGyroThreshold(0.0001f); +    }  }  Client::~Client() { -    socket->Stop(); -    thread.join(); +    Reset(); +} + +std::vector<Common::ParamPackage> Client::GetInputDevices() const { +    std::vector<Common::ParamPackage> devices; +    for (std::size_t client = 0; client < clients.size(); client++) { +        if (!DeviceConnected(client)) { +            continue; +        } +        std::string name = fmt::format("UDP Controller {}", client); +        devices.emplace_back(Common::ParamPackage{ +            {"class", "cemuhookudp"}, +            {"display", std::move(name)}, +            {"port", std::to_string(client)}, +        }); +    } +    return devices;  } +bool Client::DeviceConnected(std::size_t pad) const { +    // Use last timestamp to detect if the socket has stopped sending data +    const auto now = std::chrono::system_clock::now(); +    u64 time_difference = +        std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update) +            .count(); +    return time_difference < 1000 && clients[pad].active == 1; +} + +void Client::ReloadUDPClient() { +    for (std::size_t client = 0; client < clients.size(); client++) { +        ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client); +    } +}  void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { -    socket->Stop(); -    thread.join(); -    StartCommunication(host, port, pad_index, client_id); +    // client number must be determined from host / port and pad index +    std::size_t client = pad_index; +    clients[client].socket->Stop(); +    clients[client].thread.join(); +    StartCommunication(client, host, port, pad_index, client_id);  }  void Client::OnVersion(Response::Version data) { @@ -157,23 +194,39 @@ void Client::OnPortInfo(Response::PortInfo data) {  }  void Client::OnPadData(Response::PadData data) { +    // client number must be determined from host / port and pad index +    std::size_t client = data.info.id;      LOG_TRACE(Input, "PadData packet received"); -    if (data.packet_counter <= packet_sequence) { +    if (data.packet_counter == clients[client].packet_sequence) {          LOG_WARNING(              Input,              "PadData packet dropped because its stale info. Current count: {} Packet count: {}", -            packet_sequence, data.packet_counter); +            clients[client].packet_sequence, data.packet_counter);          return;      } -    packet_sequence = data.packet_counter; -    // TODO: Check how the Switch handles motions and how the CemuhookUDP motion -    // directions correspond to the ones of the Switch -    Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z); -    Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll); -    { -        std::lock_guard guard(status->update_mutex); +    clients[client].active = data.info.is_pad_active; +    clients[client].packet_sequence = data.packet_counter; +    const auto now = std::chrono::system_clock::now(); +    u64 time_difference = std::chrono::duration_cast<std::chrono::microseconds>( +                              now - clients[client].last_motion_update) +                              .count(); +    clients[client].last_motion_update = now; +    Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; +    clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); +    // Gyroscope values are not it the correct scale from better joy. +    // Dividing by 312 allows us to make one full turn = 1 turn +    // This must be a configurable valued called sensitivity +    clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f); +    clients[client].motion.UpdateRotation(time_difference); +    clients[client].motion.UpdateOrientation(time_difference); +    Common::Vec3f gyroscope = clients[client].motion.GetGyroscope(); +    Common::Vec3f accelerometer = clients[client].motion.GetAcceleration(); +    Common::Vec3f rotation = clients[client].motion.GetRotations(); +    std::array<Common::Vec3f, 3> orientation = clients[client].motion.GetOrientation(); -        status->motion_status = {accel, gyro}; +    { +        std::lock_guard guard(clients[client].status.update_mutex); +        clients[client].status.motion_status = {accelerometer, gyroscope, rotation, orientation};          // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates          // between a simple "tap" and a hard press that causes the touch screen to click. @@ -182,11 +235,11 @@ void Client::OnPadData(Response::PadData data) {          float x = 0;          float y = 0; -        if (is_active && status->touch_calibration) { -            const u16 min_x = status->touch_calibration->min_x; -            const u16 max_x = status->touch_calibration->max_x; -            const u16 min_y = status->touch_calibration->min_y; -            const u16 max_y = status->touch_calibration->max_y; +        if (is_active && clients[client].status.touch_calibration) { +            const u16 min_x = clients[client].status.touch_calibration->min_x; +            const u16 max_x = clients[client].status.touch_calibration->max_x; +            const u16 min_y = clients[client].status.touch_calibration->min_y; +            const u16 max_y = clients[client].status.touch_calibration->max_y;              x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) /                  static_cast<float>(max_x - min_x); @@ -194,17 +247,80 @@ void Client::OnPadData(Response::PadData data) {                  static_cast<float>(max_y - min_y);          } -        status->touch_status = {x, y, is_active}; +        clients[client].status.touch_status = {x, y, is_active}; + +        if (configuring) { +            UpdateYuzuSettings(client, accelerometer, gyroscope, is_active); +        }      }  } -void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) { +void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, +                                u32 client_id) {      SocketCallback callback{[this](Response::Version version) { OnVersion(version); },                              [this](Response::PortInfo info) { OnPortInfo(info); },                              [this](Response::PadData data) { OnPadData(data); }};      LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); -    socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); -    thread = std::thread{SocketLoop, this->socket.get()}; +    clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); +    clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; +} + +void Client::Reset() { +    for (std::size_t client = 0; client < clients.size(); client++) { +        clients[client].socket->Stop(); +        clients[client].thread.join(); +    } +} + +void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, +                                const Common::Vec3<float>& gyro, bool touch) { +    UDPPadStatus pad; +    if (touch) { +        pad.touch = PadTouch::Click; +        pad_queue[client].Push(pad); +    } +    for (size_t i = 0; i < 3; ++i) { +        if (gyro[i] > 6.0f || gyro[i] < -6.0f) { +            pad.motion = static_cast<PadMotion>(i); +            pad.motion_value = gyro[i]; +            pad_queue[client].Push(pad); +        } +        if (acc[i] > 2.0f || acc[i] < -2.0f) { +            pad.motion = static_cast<PadMotion>(i + 3); +            pad.motion_value = acc[i]; +            pad_queue[client].Push(pad); +        } +    } +} + +void Client::BeginConfiguration() { +    for (auto& pq : pad_queue) { +        pq.Clear(); +    } +    configuring = true; +} + +void Client::EndConfiguration() { +    for (auto& pq : pad_queue) { +        pq.Clear(); +    } +    configuring = false; +} + +DeviceStatus& Client::GetPadState(std::size_t pad) { +    return clients[pad].status; +} + +const DeviceStatus& Client::GetPadState(std::size_t pad) const { +    return clients[pad].status; +} + +std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() { +    return pad_queue; +} + +const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const { +    return pad_queue;  }  void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index b8c654755..523dc6a7a 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -12,8 +12,12 @@  #include <thread>  #include <tuple>  #include "common/common_types.h" +#include "common/param_package.h"  #include "common/thread.h" +#include "common/threadsafe_queue.h"  #include "common/vector_math.h" +#include "core/frontend/input.h" +#include "input_common/motion_input.h"  namespace InputCommon::CemuhookUDP { @@ -28,9 +32,30 @@ struct PortInfo;  struct Version;  } // namespace Response +enum class PadMotion { +    GyroX, +    GyroY, +    GyroZ, +    AccX, +    AccY, +    AccZ, +    Undefined, +}; + +enum class PadTouch { +    Click, +    Undefined, +}; + +struct UDPPadStatus { +    PadTouch touch{PadTouch::Undefined}; +    PadMotion motion{PadMotion::Undefined}; +    f32 motion_value{0.0f}; +}; +  struct DeviceStatus {      std::mutex update_mutex; -    std::tuple<Common::Vec3<float>, Common::Vec3<float>> motion_status; +    Input::MotionStatus motion_status;      std::tuple<float, float, bool> touch_status;      // calibration data for scaling the device's touch area to 3ds @@ -45,22 +70,58 @@ struct DeviceStatus {  class Client {  public: -    explicit Client(std::shared_ptr<DeviceStatus> status, const std::string& host = DEFAULT_ADDR, -                    u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872); +    // Initialize the UDP client capture and read sequence +    Client(); + +    // Close and release the client      ~Client(); + +    // Used for polling +    void BeginConfiguration(); +    void EndConfiguration(); + +    std::vector<Common::ParamPackage> GetInputDevices() const; + +    bool DeviceConnected(std::size_t pad) const; +    void ReloadUDPClient();      void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0,                        u32 client_id = 24872); +    std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue(); +    const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const; + +    DeviceStatus& GetPadState(std::size_t pad); +    const DeviceStatus& GetPadState(std::size_t pad) const; +  private: +    struct ClientData { +        std::unique_ptr<Socket> socket; +        DeviceStatus status; +        std::thread thread; +        u64 packet_sequence = 0; +        u8 active; + +        // Realtime values +        // motion is initalized with PID values for drift correction on joycons +        InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f}; +        std::chrono::time_point<std::chrono::system_clock> last_motion_update; +    }; + +    // For shutting down, clear all data, join all threads, release usb +    void Reset(); +      void OnVersion(Response::Version);      void OnPortInfo(Response::PortInfo);      void OnPadData(Response::PadData); -    void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id); +    void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, +                            u32 client_id); +    void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, +                            const Common::Vec3<float>& gyro, bool touch); + +    bool configuring = false; -    std::unique_ptr<Socket> socket; -    std::shared_ptr<DeviceStatus> status; -    std::thread thread; -    u64 packet_sequence = 0; +    std::array<ClientData, 4> clients; +    std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue;  };  /// An async job allowing configuration of the touchpad calibration. diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index 4b347e47e..eba077a36 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -1,105 +1,144 @@ -// Copyright 2018 Citra Emulator Project +// Copyright 2020 yuzu Emulator Project  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. +#include <atomic> +#include <list>  #include <mutex> -#include <optional> -#include <tuple> - -#include "common/param_package.h" -#include "core/frontend/input.h" -#include "core/settings.h" +#include <utility> +#include "common/assert.h" +#include "common/threadsafe_queue.h"  #include "input_common/udp/client.h"  #include "input_common/udp/udp.h" -namespace InputCommon::CemuhookUDP { +namespace InputCommon { -class UDPTouchDevice final : public Input::TouchDevice { +class UDPMotion final : public Input::MotionDevice {  public: -    explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} -    std::tuple<float, float, bool> GetStatus() const override { -        std::lock_guard guard(status->update_mutex); -        return status->touch_status; +    UDPMotion(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) +        : ip(ip_), port(port_), pad(pad_), client(client_) {} + +    Input::MotionStatus GetStatus() const override { +        return client->GetPadState(pad).motion_status;      }  private: -    std::shared_ptr<DeviceStatus> status; +    const std::string ip; +    const int port; +    const int pad; +    CemuhookUDP::Client* client; +    mutable std::mutex mutex;  }; -class UDPMotionDevice final : public Input::MotionDevice { -public: -    explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} -    std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { -        std::lock_guard guard(status->update_mutex); -        return status->motion_status; -    } +/// A motion device factory that creates motion devices from JC Adapter +UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) +    : client(std::move(client_)) {} + +/** + * Creates motion device + * @param params contains parameters for creating the device: + *     - "port": the nth jcpad on the adapter + */ +std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { +    const std::string ip = params.Get("ip", "127.0.0.1"); +    const int port = params.Get("port", 26760); +    const int pad = params.Get("pad_index", 0); + +    return std::make_unique<UDPMotion>(ip, port, pad, client.get()); +} -private: -    std::shared_ptr<DeviceStatus> status; -}; +void UDPMotionFactory::BeginConfiguration() { +    polling = true; +    client->BeginConfiguration(); +} -class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { -public: -    explicit UDPTouchFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} - -    std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override { -        { -            std::lock_guard guard(status->update_mutex); -            status->touch_calibration = DeviceStatus::CalibrationData{}; -            // These default values work well for DS4 but probably not other touch inputs -            status->touch_calibration->min_x = params.Get("min_x", 100); -            status->touch_calibration->min_y = params.Get("min_y", 50); -            status->touch_calibration->max_x = params.Get("max_x", 1800); -            status->touch_calibration->max_y = params.Get("max_y", 850); +void UDPMotionFactory::EndConfiguration() { +    polling = false; +    client->EndConfiguration(); +} + +Common::ParamPackage UDPMotionFactory::GetNextInput() { +    Common::ParamPackage params; +    CemuhookUDP::UDPPadStatus pad; +    auto& queue = client->GetPadQueue(); +    for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { +        while (queue[pad_number].Pop(pad)) { +            if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) { +                continue; +            } +            params.Set("engine", "cemuhookudp"); +            params.Set("ip", "127.0.0.1"); +            params.Set("port", 26760); +            params.Set("pad_index", static_cast<int>(pad_number)); +            params.Set("motion", static_cast<u16>(pad.motion)); +            return params;          } -        return std::make_unique<UDPTouchDevice>(status);      } +    return params; +} -private: -    std::shared_ptr<DeviceStatus> status; -}; - -class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> { +class UDPTouch final : public Input::TouchDevice {  public: -    explicit UDPMotionFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} +    UDPTouch(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) +        : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} -    std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override { -        return std::make_unique<UDPMotionDevice>(status); +    std::tuple<float, float, bool> GetStatus() const override { +        return client->GetPadState(pad).touch_status;      }  private: -    std::shared_ptr<DeviceStatus> status; +    const std::string ip; +    const int port; +    const int pad; +    CemuhookUDP::Client* client; +    mutable std::mutex mutex;  }; -State::State() { -    auto status = std::make_shared<DeviceStatus>(); -    client = -        std::make_unique<Client>(status, Settings::values.udp_input_address, -                                 Settings::values.udp_input_port, Settings::values.udp_pad_index); - -    motion_factory = std::make_shared<UDPMotionFactory>(status); -    touch_factory = std::make_shared<UDPTouchFactory>(status); - -    Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory); -    Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory); +/// A motion device factory that creates motion devices from JC Adapter +UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) +    : client(std::move(client_)) {} + +/** + * Creates motion device + * @param params contains parameters for creating the device: + *     - "port": the nth jcpad on the adapter + */ +std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { +    const std::string ip = params.Get("ip", "127.0.0.1"); +    const int port = params.Get("port", 26760); +    const int pad = params.Get("pad_index", 0); + +    return std::make_unique<UDPTouch>(ip, port, pad, client.get());  } -State::~State() { -    Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); -    Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); +void UDPTouchFactory::BeginConfiguration() { +    polling = true; +    client->BeginConfiguration();  } -std::vector<Common::ParamPackage> State::GetInputDevices() const { -    // TODO support binding udp devices -    return {}; +void UDPTouchFactory::EndConfiguration() { +    polling = false; +    client->EndConfiguration();  } -void State::ReloadUDPClient() { -    client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, -                         Settings::values.udp_pad_index); +Common::ParamPackage UDPTouchFactory::GetNextInput() { +    Common::ParamPackage params; +    CemuhookUDP::UDPPadStatus pad; +    auto& queue = client->GetPadQueue(); +    for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { +        while (queue[pad_number].Pop(pad)) { +            if (pad.touch == CemuhookUDP::PadTouch::Undefined) { +                continue; +            } +            params.Set("engine", "cemuhookudp"); +            params.Set("ip", "127.0.0.1"); +            params.Set("port", 26760); +            params.Set("pad_index", static_cast<int>(pad_number)); +            params.Set("touch", static_cast<u16>(pad.touch)); +            return params; +        } +    } +    return params;  } -std::unique_ptr<State> Init() { -    return std::make_unique<State>(); -} -} // namespace InputCommon::CemuhookUDP +} // namespace InputCommon diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h index 672a5c812..ea3fd4175 100644 --- a/src/input_common/udp/udp.h +++ b/src/input_common/udp/udp.h @@ -1,32 +1,57 @@ -// Copyright 2018 Citra Emulator Project +// Copyright 2020 yuzu Emulator Project  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included.  #pragma once  #include <memory> -#include <vector> -#include "common/param_package.h" +#include "core/frontend/input.h" +#include "input_common/udp/client.h" -namespace InputCommon::CemuhookUDP { +namespace InputCommon { -class Client; -class UDPMotionFactory; -class UDPTouchFactory; - -class State { +/// A motion device factory that creates motion devices from udp clients +class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {  public: -    State(); -    ~State(); -    void ReloadUDPClient(); -    std::vector<Common::ParamPackage> GetInputDevices() const; +    explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_); + +    std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override; + +    Common::ParamPackage GetNextInput(); + +    /// For device input configuration/polling +    void BeginConfiguration(); +    void EndConfiguration(); + +    bool IsPolling() const { +        return polling; +    }  private: -    std::unique_ptr<Client> client; -    std::shared_ptr<UDPMotionFactory> motion_factory; -    std::shared_ptr<UDPTouchFactory> touch_factory; +    std::shared_ptr<CemuhookUDP::Client> client; +    bool polling = false;  }; -std::unique_ptr<State> Init(); +/// A touch device factory that creates touch devices from udp clients +class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { +public: +    explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_); + +    std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override; + +    Common::ParamPackage GetNextInput(); + +    /// For device input configuration/polling +    void BeginConfiguration(); +    void EndConfiguration(); + +    bool IsPolling() const { +        return polling; +    } + +private: +    std::shared_ptr<CemuhookUDP::Client> client; +    bool polling = false; +}; -} // namespace InputCommon::CemuhookUDP +} // namespace InputCommon | 
