diff --git a/src/common/thread.h b/src/common/thread.h index fa475ab51..db196c05f 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -55,6 +55,15 @@ public: is_set = false; } + template + bool WaitFor(const std::chrono::duration& time) { + std::unique_lock lk(mutex); + if (!condvar.wait_for(lk, time, [this] { return is_set; })) + return false; + is_set = false; + return true; + } + template bool WaitUntil(const std::chrono::time_point& time) { std::unique_lock lk(mutex); diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index c751bdeb0..d842296d3 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -76,6 +76,11 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, return circle_pad_param.Serialize(); } +void ReloadInputDevices() { + if (udp) + udp->ReloadUDPClient(); +} + namespace Polling { std::vector> GetPollers(DeviceType type) { diff --git a/src/input_common/main.h b/src/input_common/main.h index 77a0ce90b..d1229b207 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -37,6 +37,9 @@ std::string GenerateKeyboardParam(int key_code); std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, int key_modifier, float modifier_scale); +/// Reloads the input devices +void ReloadInputDevices(); + namespace Polling { enum class DeviceType { Button, Analog }; diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 9f668d233..c0224bc6a 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -11,7 +11,6 @@ #include #include #include "common/logging/log.h" -#include "common/vector_math.h" #include "input_common/udp/client.h" #include "input_common/udp/protocol.h" @@ -128,12 +127,7 @@ static void SocketLoop(Socket* socket) { Client::Client(std::shared_ptr status, const std::string& host, u16 port, u8 pad_index, u32 client_id) : status(status) { - 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(host, port, pad_index, client_id, callback); - thread = std::thread{SocketLoop, this->socket.get()}; + StartCommunication(host, port, pad_index, client_id); } Client::~Client() { @@ -141,6 +135,12 @@ Client::~Client() { thread.join(); } +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); +} + void Client::OnVersion(Response::Version data) { LOG_TRACE(Input, "Version packet received: {}", data.version); } @@ -192,4 +192,93 @@ void Client::OnPadData(Response::PadData data) { } } +void Client::StartCommunication(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(host, port, pad_index, client_id, callback); + thread = std::thread{SocketLoop, this->socket.get()}; +} + +void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, + std::function success_callback, + std::function failure_callback) { + std::thread([=] { + Common::Event success_event; + SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, + [&](Response::PadData data) { success_event.Set(); }}; + Socket socket{host, port, pad_index, client_id, callback}; + std::thread worker_thread{SocketLoop, &socket}; + bool result = success_event.WaitFor(std::chrono::seconds(8)); + socket.Stop(); + worker_thread.join(); + if (result) + success_callback(); + else + failure_callback(); + }) + .detach(); +} + +CalibrationConfigurationJob::CalibrationConfigurationJob( + const std::string& host, u16 port, u8 pad_index, u32 client_id, + std::function status_callback, + std::function data_callback) { + + std::thread([=] { + constexpr u16 CALIBRATION_THRESHOLD = 100; + + u16 min_x{UINT16_MAX}, min_y{UINT16_MAX}; + u16 max_x, max_y; + + Status current_status{Status::Initialized}; + SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, + [&](Response::PadData data) { + if (current_status == Status::Initialized) { + // Receiving data means the communication is ready now + current_status = Status::Ready; + status_callback(current_status); + } + if (!data.touch_1.is_active) + return; + LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, + data.touch_1.y); + min_x = std::min(min_x, static_cast(data.touch_1.x)); + min_y = std::min(min_y, static_cast(data.touch_1.y)); + if (current_status == Status::Ready) { + // First touch - min data (min_x/min_y) + current_status = Status::Stage1Completed; + status_callback(current_status); + } + if (data.touch_1.x - min_x > CALIBRATION_THRESHOLD && + data.touch_1.y - min_y > CALIBRATION_THRESHOLD) { + // Set the current position as max value and finishes + // configuration + max_x = data.touch_1.x; + max_y = data.touch_1.y; + current_status = Status::Completed; + data_callback(min_x, min_y, max_x, max_y); + status_callback(current_status); + + complete_event.Set(); + } + }}; + Socket socket{host, port, pad_index, client_id, callback}; + std::thread worker_thread{SocketLoop, &socket}; + complete_event.Wait(); + socket.Stop(); + worker_thread.join(); + }) + .detach(); +} + +CalibrationConfigurationJob::~CalibrationConfigurationJob() { + Stop(); +} + +void CalibrationConfigurationJob::Stop() { + complete_event.Set(); +} + } // namespace InputCommon::CemuhookUDP diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 0dca3b28a..a16ea34e4 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -12,6 +12,7 @@ #include #include #include "common/common_types.h" +#include "common/thread.h" #include "common/vector_math.h" namespace InputCommon::CemuhookUDP { @@ -47,15 +48,48 @@ public: explicit Client(std::shared_ptr status, const std::string& host = DEFAULT_ADDR, u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872); ~Client(); + void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0, + u32 client_id = 24872); private: 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); std::unique_ptr socket; std::shared_ptr status; std::thread thread; u64 packet_sequence = 0; }; + +/// An async job allowing configuration of the touchpad calibration. +class CalibrationConfigurationJob { +public: + enum class Status { + Initialized, + Ready, + Stage1Completed, + Completed, + }; + /** + * Constructs and starts the job with the specified parameter. + * + * @param status_callback Callback for job status updates + * @param data_callback Called when calibration data is ready + */ + explicit CalibrationConfigurationJob(const std::string& host, u16 port, u8 pad_index, + u32 client_id, std::function status_callback, + std::function data_callback); + ~CalibrationConfigurationJob(); + void Stop(); + +private: + Common::Event complete_event; +}; + +void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, + std::function success_callback, + std::function failure_callback); + } // namespace InputCommon::CemuhookUDP diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index 9e32d9fd9..edb3fd5ad 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -85,6 +85,11 @@ State::~State() { Input::UnregisterFactory("cemuhookudp"); } +void State::ReloadUDPClient() { + client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, + Settings::values.udp_pad_index); +} + std::unique_ptr Init() { return std::make_unique(); } diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h index 3a460c7ba..ea3de60bb 100644 --- a/src/input_common/udp/udp.h +++ b/src/input_common/udp/udp.h @@ -16,6 +16,7 @@ class State { public: State(); ~State(); + void ReloadUDPClient(); private: std::unique_ptr client;