From 10241886ddbead1a24a5924debf83a4fad0ade0d Mon Sep 17 00:00:00 2001 From: german77 Date: Mon, 20 Sep 2021 17:33:19 -0500 Subject: [PATCH] input_common: Rewrite udp client --- src/input_common/CMakeLists.txt | 6 +- .../client.cpp => drivers/udp_client.cpp} | 240 +++--------------- .../{udp/client.h => drivers/udp_client.h} | 80 +----- src/input_common/udp/udp.cpp | 110 -------- src/input_common/udp/udp.h | 57 ----- 5 files changed, 53 insertions(+), 440 deletions(-) rename src/input_common/{udp/client.cpp => drivers/udp_client.cpp} (60%) rename src/input_common/{udp/client.h => drivers/udp_client.h} (56%) delete mode 100644 src/input_common/udp/udp.cpp delete mode 100644 src/input_common/udp/udp.h diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 19270dc84..9c0c82138 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -9,6 +9,8 @@ add_library(input_common STATIC drivers/tas_input.h drivers/touch_screen.cpp drivers/touch_screen.h + drivers/udp_client.cpp + drivers/udp_client.h helpers/stick_from_buttons.cpp helpers/stick_from_buttons.h helpers/touch_from_buttons.cpp @@ -29,10 +31,6 @@ add_library(input_common STATIC motion_input.h sdl/sdl.cpp sdl/sdl.h - udp/client.cpp - udp/client.h - udp/udp.cpp - udp/udp.h ) if (MSVC) diff --git a/src/input_common/udp/client.cpp b/src/input_common/drivers/udp_client.cpp similarity index 60% rename from src/input_common/udp/client.cpp rename to src/input_common/drivers/udp_client.cpp index bcc29c4e0..6fcc3a01b 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -2,15 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include -#include #include -#include #include + #include "common/logging/log.h" +#include "common/param_package.h" #include "common/settings.h" -#include "input_common/udp/client.h" +#include "input_common/drivers/udp_client.h" #include "input_common/helpers/udp_protocol.h" using boost::asio::ip::udp; @@ -86,7 +84,6 @@ private: case Type::PadData: { Response::PadData pad_data; std::memcpy(&pad_data, &receive_buffer[sizeof(Header)], sizeof(Response::PadData)); - SanitizeMotion(pad_data); callback.pad_data(std::move(pad_data)); break; } @@ -115,28 +112,6 @@ private: StartSend(timer.expiry()); } - void SanitizeMotion(Response::PadData& data) { - // Zero out any non number value - if (!std::isnormal(data.gyro.pitch)) { - data.gyro.pitch = 0; - } - if (!std::isnormal(data.gyro.roll)) { - data.gyro.roll = 0; - } - if (!std::isnormal(data.gyro.yaw)) { - data.gyro.yaw = 0; - } - if (!std::isnormal(data.accel.x)) { - data.accel.x = 0; - } - if (!std::isnormal(data.accel.y)) { - data.accel.y = 0; - } - if (!std::isnormal(data.accel.z)) { - data.accel.z = 0; - } - } - SocketCallback callback; boost::asio::io_service io_service; boost::asio::basic_waitable_timer timer; @@ -160,48 +135,23 @@ static void SocketLoop(Socket* socket) { socket->Loop(); } -Client::Client() { +UDPClient::UDPClient(const std::string& input_engine_) : InputEngine(input_engine_) { LOG_INFO(Input, "Udp Initialization started"); - finger_id.fill(MAX_TOUCH_FINGERS); ReloadSockets(); } -Client::~Client() { +UDPClient::~UDPClient() { Reset(); } -Client::ClientConnection::ClientConnection() = default; +UDPClient::ClientConnection::ClientConnection() = default; -Client::ClientConnection::~ClientConnection() = default; +UDPClient::ClientConnection::~ClientConnection() = default; -std::vector Client::GetInputDevices() const { - std::vector devices; - for (std::size_t pad = 0; pad < pads.size(); pad++) { - if (!DeviceConnected(pad)) { - continue; - } - std::string name = fmt::format("UDP Controller {}", pad); - devices.emplace_back(Common::ParamPackage{ - {"class", "cemuhookudp"}, - {"display", std::move(name)}, - {"port", std::to_string(pad)}, - }); - } - 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::steady_clock::now(); - const auto time_difference = static_cast( - std::chrono::duration_cast(now - pads[pad].last_update).count()); - return time_difference < 1000 && pads[pad].connected; -} - -void Client::ReloadSockets() { +void UDPClient::ReloadSockets() { Reset(); - std::stringstream servers_ss(static_cast(Settings::values.udp_input_servers)); + std::stringstream servers_ss(Settings::values.udp_input_servers.GetValue()); std::string server_token; std::size_t client = 0; while (std::getline(servers_ss, server_token, ',')) { @@ -229,7 +179,7 @@ void Client::ReloadSockets() { } } -std::size_t Client::GetClientNumber(std::string_view host, u16 port) const { +std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const { for (std::size_t client = 0; client < clients.size(); client++) { if (clients[client].active == -1) { continue; @@ -241,15 +191,15 @@ std::size_t Client::GetClientNumber(std::string_view host, u16 port) const { return MAX_UDP_CLIENTS; } -void Client::OnVersion([[maybe_unused]] Response::Version data) { +void UDPClient::OnVersion([[maybe_unused]] Response::Version data) { LOG_TRACE(Input, "Version packet received: {}", data.version); } -void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) { +void UDPClient::OnPortInfo([[maybe_unused]] Response::PortInfo data) { LOG_TRACE(Input, "PortInfo packet received: {}", data.model); } -void Client::OnPadData(Response::PadData data, std::size_t client) { +void UDPClient::OnPadData(Response::PadData data, std::size_t client) { const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id; if (pad_index >= pads.size()) { @@ -277,32 +227,25 @@ void Client::OnPadData(Response::PadData data, std::size_t client) { .count()); pads[pad_index].last_update = now; - const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; - pads[pad_index].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 - pads[pad_index].motion.SetGyroscope(raw_gyroscope / 312.0f); - pads[pad_index].motion.UpdateRotation(time_difference); - pads[pad_index].motion.UpdateOrientation(time_difference); + const float gyro_scale = 1.0f / 312.0f; - { - std::lock_guard guard(pads[pad_index].status.update_mutex); - pads[pad_index].status.motion_status = pads[pad_index].motion.GetMotion(); - - for (std::size_t id = 0; id < data.touch.size(); ++id) { - UpdateTouchInput(data.touch[id], client, id); - } - - if (configuring) { - const Common::Vec3f gyroscope = pads[pad_index].motion.GetGyroscope(); - const Common::Vec3f accelerometer = pads[pad_index].motion.GetAcceleration(); - UpdateYuzuSettings(client, data.info.id, accelerometer, gyroscope); - } - } + const BasicMotion motion{ + .gyro_x = data.gyro.pitch * gyro_scale, + .gyro_y = data.gyro.roll * gyro_scale, + .gyro_z = -data.gyro.yaw * gyro_scale, + .accel_x = data.accel.x, + .accel_y = -data.accel.z, + .accel_z = data.accel.y, + .delta_timestamp = time_difference, + }; + const PadIdentifier identifier = GetPadIdentifier(pad_index); + SetMotion(identifier, 0, motion); } -void Client::StartCommunication(std::size_t client, const std::string& host, u16 port) { +void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) { SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, [this](Response::PortInfo info) { OnPortInfo(info); }, [this, client](Response::PadData data) { OnPadData(data, client); }}; @@ -312,16 +255,22 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16 clients[client].active = 0; clients[client].socket = std::make_unique(host, port, callback); clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; - - // Set motion parameters - // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode - // Real HW values are unknown, 0.0001 is an approximate to Standard - for (std::size_t pad = 0; pad < PADS_PER_CLIENT; pad++) { - pads[client * PADS_PER_CLIENT + pad].motion.SetGyroThreshold(0.0001f); + for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) { + const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index); + PreSetController(identifier); } } -void Client::Reset() { +const PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const { + const std::size_t client = pad_index / PADS_PER_CLIENT; + return { + .guid = Common::UUID{clients[client].host}, + .port = static_cast(clients[client].port), + .pad = pad_index, + }; +} + +void UDPClient::Reset() { for (auto& client : clients) { if (client.thread.joinable()) { client.active = -1; @@ -331,117 +280,6 @@ void Client::Reset() { } } -void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index, - const Common::Vec3& acc, const Common::Vec3& gyro) { - if (gyro.Length() > 0.2f) { - LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client, - gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]); - } - UDPPadStatus pad{ - .host = clients[client].host, - .port = clients[client].port, - .pad_index = pad_index, - }; - for (std::size_t i = 0; i < 3; ++i) { - if (gyro[i] > 5.0f || gyro[i] < -5.0f) { - pad.motion = static_cast(i); - pad.motion_value = gyro[i]; - pad_queue.Push(pad); - } - if (acc[i] > 1.75f || acc[i] < -1.75f) { - pad.motion = static_cast(i + 3); - pad.motion_value = acc[i]; - pad_queue.Push(pad); - } - } -} - -std::optional Client::GetUnusedFingerID() const { - std::size_t first_free_id = 0; - while (first_free_id < MAX_TOUCH_FINGERS) { - if (!std::get<2>(touch_status[first_free_id])) { - return first_free_id; - } else { - first_free_id++; - } - } - return std::nullopt; -} - -void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) { - // TODO: Use custom calibration per device - const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); - const u16 min_x = static_cast(touch_param.Get("min_x", 100)); - const u16 min_y = static_cast(touch_param.Get("min_y", 50)); - const u16 max_x = static_cast(touch_param.Get("max_x", 1800)); - const u16 max_y = static_cast(touch_param.Get("max_y", 850)); - const std::size_t touch_id = client * 2 + id; - if (touch_pad.is_active) { - if (finger_id[touch_id] == MAX_TOUCH_FINGERS) { - const auto first_free_id = GetUnusedFingerID(); - if (!first_free_id) { - // Invalid finger id skip to next input - return; - } - finger_id[touch_id] = *first_free_id; - } - auto& [x, y, pressed] = touch_status[finger_id[touch_id]]; - x = static_cast(std::clamp(static_cast(touch_pad.x), min_x, max_x) - min_x) / - static_cast(max_x - min_x); - y = static_cast(std::clamp(static_cast(touch_pad.y), min_y, max_y) - min_y) / - static_cast(max_y - min_y); - pressed = true; - return; - } - - if (finger_id[touch_id] != MAX_TOUCH_FINGERS) { - touch_status[finger_id[touch_id]] = {}; - finger_id[touch_id] = MAX_TOUCH_FINGERS; - } -} - -void Client::BeginConfiguration() { - pad_queue.Clear(); - configuring = true; -} - -void Client::EndConfiguration() { - pad_queue.Clear(); - configuring = false; -} - -DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) { - const std::size_t client_number = GetClientNumber(host, port); - if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) { - return pads[0].status; - } - return pads[(client_number * PADS_PER_CLIENT) + pad].status; -} - -const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const { - const std::size_t client_number = GetClientNumber(host, port); - if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) { - return pads[0].status; - } - return pads[(client_number * PADS_PER_CLIENT) + pad].status; -} - -Input::TouchStatus& Client::GetTouchState() { - return touch_status; -} - -const Input::TouchStatus& Client::GetTouchState() const { - return touch_status; -} - -Common::SPSCQueue& Client::GetPadQueue() { - return pad_queue; -} - -const Common::SPSCQueue& Client::GetPadQueue() const { - return pad_queue; -} - void TestCommunication(const std::string& host, u16 port, const std::function& success_callback, const std::function& failure_callback) { diff --git a/src/input_common/udp/client.h b/src/input_common/drivers/udp_client.h similarity index 56% rename from src/input_common/udp/client.h rename to src/input_common/drivers/udp_client.h index 380f9bb76..58b2e921d 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/drivers/udp_client.h @@ -1,23 +1,14 @@ -// Copyright 2018 Citra Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// Refer to the license.txt file included #pragma once -#include -#include -#include #include -#include -#include -#include + #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" +#include "input_common/input_engine.h" namespace InputCommon::CemuhookUDP { @@ -30,16 +21,6 @@ struct TouchPad; struct Version; } // namespace Response -enum class PadMotion { - GyroX, - GyroY, - GyroZ, - AccX, - AccY, - AccZ, - Undefined, -}; - enum class PadTouch { Click, Undefined, @@ -49,14 +30,10 @@ struct UDPPadStatus { std::string host{"127.0.0.1"}; u16 port{26760}; std::size_t pad_index{}; - PadMotion motion{PadMotion::Undefined}; - f32 motion_value{0.0f}; }; struct DeviceStatus { std::mutex update_mutex; - Input::MotionStatus motion_status; - std::tuple touch_status; // calibration data for scaling the device's touch area to 3ds struct CalibrationData { @@ -68,32 +45,17 @@ struct DeviceStatus { std::optional touch_calibration; }; -class Client { +/** + * A button device factory representing a keyboard. It receives keyboard events and forward them + * to all button devices it created. + */ +class UDPClient final : public InputCommon::InputEngine { public: - // Initialize the UDP client capture and read sequence - Client(); + explicit UDPClient(const std::string& input_engine_); + ~UDPClient(); - // Close and release the client - ~Client(); - - // Used for polling - void BeginConfiguration(); - void EndConfiguration(); - - std::vector GetInputDevices() const; - - bool DeviceConnected(std::size_t pad) const; void ReloadSockets(); - Common::SPSCQueue& GetPadQueue(); - const Common::SPSCQueue& GetPadQueue() const; - - DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad); - const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const; - - Input::TouchStatus& GetTouchState(); - const Input::TouchStatus& GetTouchState() const; - private: struct PadData { std::size_t pad_index{}; @@ -101,9 +63,6 @@ private: DeviceStatus status; u64 packet_sequence{}; - // 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 last_update; }; @@ -127,28 +86,13 @@ private: void OnPortInfo(Response::PortInfo); void OnPadData(Response::PadData, std::size_t client); void StartCommunication(std::size_t client, const std::string& host, u16 port); - void UpdateYuzuSettings(std::size_t client, std::size_t pad_index, - const Common::Vec3& acc, const Common::Vec3& gyro); - - // Returns an unused finger id, if there is no fingers available std::nullopt will be - // returned - std::optional GetUnusedFingerID() const; - - // Merges and updates all touch inputs into the touch_status array - void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id); - - bool configuring = false; + const PadIdentifier GetPadIdentifier(std::size_t pad_index) const; // Allocate clients for 8 udp servers static constexpr std::size_t MAX_UDP_CLIENTS = 8; static constexpr std::size_t PADS_PER_CLIENT = 4; - // Each client can have up 2 touch inputs - static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2; std::array pads{}; std::array clients{}; - Common::SPSCQueue pad_queue{}; - Input::TouchStatus touch_status{}; - std::array finger_id{}; }; /// 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 deleted file mode 100644 index 9829da6f0..000000000 --- a/src/input_common/udp/udp.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include "common/assert.h" -#include "common/threadsafe_queue.h" -#include "input_common/udp/client.h" -#include "input_common/udp/udp.h" - -namespace InputCommon { - -class UDPMotion final : public Input::MotionDevice { -public: - explicit UDPMotion(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_) - : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} - - Input::MotionStatus GetStatus() const override { - return client->GetPadState(ip, port, pad).motion_status; - } - -private: - const std::string ip; - const u16 port; - const u16 pad; - CemuhookUDP::Client* client; - mutable std::mutex mutex; -}; - -/// A motion device factory that creates motion devices from a UDP client -UDPMotionFactory::UDPMotionFactory(std::shared_ptr client_) - : client(std::move(client_)) {} - -/** - * Creates motion device - * @param params contains parameters for creating the device: - * - "port": the UDP port number - */ -std::unique_ptr UDPMotionFactory::Create(const Common::ParamPackage& params) { - auto ip = params.Get("ip", "127.0.0.1"); - const auto port = static_cast(params.Get("port", 26760)); - const auto pad = static_cast(params.Get("pad_index", 0)); - - return std::make_unique(std::move(ip), port, pad, client.get()); -} - -void UDPMotionFactory::BeginConfiguration() { - polling = true; - client->BeginConfiguration(); -} - -void UDPMotionFactory::EndConfiguration() { - polling = false; - client->EndConfiguration(); -} - -Common::ParamPackage UDPMotionFactory::GetNextInput() { - Common::ParamPackage params; - CemuhookUDP::UDPPadStatus pad; - auto& queue = client->GetPadQueue(); - while (queue.Pop(pad)) { - if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) { - continue; - } - params.Set("engine", "cemuhookudp"); - params.Set("ip", pad.host); - params.Set("port", static_cast(pad.port)); - params.Set("pad_index", static_cast(pad.pad_index)); - params.Set("motion", static_cast(pad.motion)); - return params; - } - return params; -} - -class UDPTouch final : public Input::TouchDevice { -public: - explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_) - : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} - - Input::TouchStatus GetStatus() const override { - return client->GetTouchState(); - } - -private: - const std::string ip; - [[maybe_unused]] const u16 port; - [[maybe_unused]] const u16 pad; - CemuhookUDP::Client* client; - mutable std::mutex mutex; -}; - -/// A motion device factory that creates motion devices from a UDP client -UDPTouchFactory::UDPTouchFactory(std::shared_ptr client_) - : client(std::move(client_)) {} - -/** - * Creates motion device - * @param params contains parameters for creating the device: - * - "port": the UDP port number - */ -std::unique_ptr UDPTouchFactory::Create(const Common::ParamPackage& params) { - auto ip = params.Get("ip", "127.0.0.1"); - const auto port = static_cast(params.Get("port", 26760)); - const auto pad = static_cast(params.Get("pad_index", 0)); - - return std::make_unique(std::move(ip), port, pad, client.get()); -} - -} // namespace InputCommon diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h deleted file mode 100644 index ea3fd4175..000000000 --- a/src/input_common/udp/udp.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include "core/frontend/input.h" -#include "input_common/udp/client.h" - -namespace InputCommon { - -/// A motion device factory that creates motion devices from udp clients -class UDPMotionFactory final : public Input::Factory { -public: - explicit UDPMotionFactory(std::shared_ptr client_); - - std::unique_ptr 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 client; - bool polling = false; -}; - -/// A touch device factory that creates touch devices from udp clients -class UDPTouchFactory final : public Input::Factory { -public: - explicit UDPTouchFactory(std::shared_ptr client_); - - std::unique_ptr 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 client; - bool polling = false; -}; - -} // namespace InputCommon