input_common/udp: Port various changes from yuzu (#5133)

This commit is contained in:
Tobias 2020-04-17 02:50:28 +02:00 committed by GitHub
parent 026a63bcf7
commit 32cbb1bc77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 82 deletions

View File

@ -30,7 +30,7 @@ public:
template <class Duration> template <class Duration>
bool WaitFor(const std::chrono::duration<Duration>& time) { bool WaitFor(const std::chrono::duration<Duration>& time) {
std::unique_lock lk(mutex); std::unique_lock lk{mutex};
if (!condvar.wait_for(lk, time, [this] { return is_set; })) if (!condvar.wait_for(lk, time, [this] { return is_set; }))
return false; return false;
is_set = false; is_set = false;

View File

@ -32,8 +32,16 @@ public:
SocketCallback callback) SocketCallback callback)
: callback(std::move(callback)), timer(io_service), : callback(std::move(callback)), timer(io_service),
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id),
pad_index(pad_index), pad_index(pad_index) {
send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {} boost::system::error_code ec{};
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
if (ec.value() != boost::system::errc::success) {
LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
ipv4 = boost::asio::ip::address_v4{};
}
send_endpoint = {udp::endpoint(ipv4, port)};
}
void Stop() { void Stop() {
io_service.stop(); io_service.stop();
@ -85,17 +93,18 @@ private:
} }
void HandleSend(const boost::system::error_code& error) { void HandleSend(const boost::system::error_code& error) {
boost::system::error_code _ignored{};
// Send a request for getting port info for the pad // Send a request for getting port info for the pad
Request::PortInfo port_info{1, {pad_index, 0, 0, 0}}; Request::PortInfo port_info{1, {pad_index, 0, 0, 0}};
auto port_message = Request::Create(port_info, client_id); const auto port_message = Request::Create(port_info, client_id);
std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
std::size_t len = socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint); socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
// Send a request for getting pad data for the pad // Send a request for getting pad data for the pad
Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS}; Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS};
auto pad_message = Request::Create(pad_data, client_id); const auto pad_message = Request::Create(pad_data, client_id);
std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE); std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
std::size_t len2 = socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint); socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
StartSend(timer.expiry()); StartSend(timer.expiry());
} }
@ -104,8 +113,8 @@ private:
boost::asio::basic_waitable_timer<clock> timer; boost::asio::basic_waitable_timer<clock> timer;
udp::socket socket; udp::socket socket;
u32 client_id; u32 client_id{};
u8 pad_index; u8 pad_index{};
static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>); static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
@ -170,16 +179,16 @@ void Client::OnPadData(Response::PadData data) {
// TODO: add a setting for "click" touch. Click touch refers to a device that differentiates // 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. // between a simple "tap" and a hard press that causes the touch screen to click.
bool is_active = data.touch_1.is_active != 0; const bool is_active = data.touch_1.is_active != 0;
float x = 0; float x = 0;
float y = 0; float y = 0;
if (is_active && status->touch_calibration) { if (is_active && status->touch_calibration) {
u16 min_x = status->touch_calibration->min_x; const u16 min_x = status->touch_calibration->min_x;
u16 max_x = status->touch_calibration->max_x; const u16 max_x = status->touch_calibration->max_x;
u16 min_y = status->touch_calibration->min_y; const u16 min_y = status->touch_calibration->min_y;
u16 max_y = status->touch_calibration->max_y; const u16 max_y = status->touch_calibration->max_y;
x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) / x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) /
static_cast<float>(max_x - min_x); static_cast<float>(max_x - min_x);
@ -212,10 +221,11 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
bool result = success_event.WaitFor(std::chrono::seconds(8)); bool result = success_event.WaitFor(std::chrono::seconds(8));
socket.Stop(); socket.Stop();
worker_thread.join(); worker_thread.join();
if (result) if (result) {
success_callback(); success_callback();
else } else {
failure_callback(); failure_callback();
}
}) })
.detach(); .detach();
} }
@ -228,8 +238,10 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
std::thread([=] { std::thread([=] {
constexpr u16 CALIBRATION_THRESHOLD = 100; constexpr u16 CALIBRATION_THRESHOLD = 100;
u16 min_x{UINT16_MAX}, min_y{UINT16_MAX}; u16 min_x{UINT16_MAX};
u16 max_x, max_y; u16 min_y{UINT16_MAX};
u16 max_x{};
u16 max_y{};
Status current_status{Status::Initialized}; Status current_status{Status::Initialized};
SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {},
@ -239,8 +251,9 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
current_status = Status::Ready; current_status = Status::Ready;
status_callback(current_status); status_callback(current_status);
} }
if (!data.touch_1.is_active) if (!data.touch_1.is_active) {
return; return;
}
LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x,
data.touch_1.y); data.touch_1.y);
min_x = std::min(min_x, static_cast<u16>(data.touch_1.x)); min_x = std::min(min_x, static_cast<u16>(data.touch_1.x));

View File

@ -17,8 +17,8 @@
namespace InputCommon::CemuhookUDP { namespace InputCommon::CemuhookUDP {
static constexpr u16 DEFAULT_PORT = 26760; constexpr u16 DEFAULT_PORT = 26760;
static constexpr const char* DEFAULT_ADDR = "127.0.0.1"; constexpr char DEFAULT_ADDR[] = "127.0.0.1";
class Socket; class Socket;
@ -35,10 +35,10 @@ struct DeviceStatus {
// calibration data for scaling the device's touch area to 3ds // calibration data for scaling the device's touch area to 3ds
struct CalibrationData { struct CalibrationData {
u16 min_x; u16 min_x{};
u16 min_y; u16 min_y{};
u16 max_x; u16 max_x{};
u16 max_y; u16 max_y{};
}; };
std::optional<CalibrationData> touch_calibration; std::optional<CalibrationData> touch_calibration;
}; };

View File

@ -9,7 +9,7 @@
namespace InputCommon::CemuhookUDP { namespace InputCommon::CemuhookUDP {
static const std::size_t GetSizeOfResponseType(Type t) { static constexpr std::size_t GetSizeOfResponseType(Type t) {
switch (t) { switch (t) {
case Type::Version: case Type::Version:
return sizeof(Response::Version); return sizeof(Response::Version);
@ -31,22 +31,21 @@ namespace Response {
*/ */
std::optional<Type> Validate(u8* data, std::size_t size) { std::optional<Type> Validate(u8* data, std::size_t size) {
if (size < sizeof(Header)) { if (size < sizeof(Header)) {
LOG_DEBUG(Input, "Invalid UDP packet received"); return std::nullopt;
return {};
} }
Header header; Header header{};
std::memcpy(&header, data, sizeof(Header)); std::memcpy(&header, data, sizeof(Header));
if (header.magic != SERVER_MAGIC) { if (header.magic != SERVER_MAGIC) {
LOG_ERROR(Input, "UDP Packet has an unexpected magic value"); LOG_ERROR(Input, "UDP Packet has an unexpected magic value");
return {}; return std::nullopt;
} }
if (header.protocol_version != PROTOCOL_VERSION) { if (header.protocol_version != PROTOCOL_VERSION) {
LOG_ERROR(Input, "UDP Packet protocol mismatch"); LOG_ERROR(Input, "UDP Packet protocol mismatch");
return {}; return std::nullopt;
} }
if (header.type < Type::Version || header.type > Type::PadData) { if (header.type < Type::Version || header.type > Type::PadData) {
LOG_ERROR(Input, "UDP Packet is an unknown type"); LOG_ERROR(Input, "UDP Packet is an unknown type");
return {}; return std::nullopt;
} }
// Packet size must equal sizeof(Header) + sizeof(Data) // Packet size must equal sizeof(Header) + sizeof(Data)
@ -59,7 +58,7 @@ std::optional<Type> Validate(u8* data, std::size_t size) {
Input, Input,
"UDP Packet payload length doesn't match. Received: {} PayloadLength: {} Expected: {}", "UDP Packet payload length doesn't match. Received: {} PayloadLength: {} Expected: {}",
size, header.payload_length, data_len + sizeof(Type)); size, header.payload_length, data_len + sizeof(Type));
return {}; return std::nullopt;
} }
const u32 crc32 = header.crc; const u32 crc32 = header.crc;
@ -70,7 +69,7 @@ std::optional<Type> Validate(u8* data, std::size_t size) {
result.process_bytes(data, data_len + sizeof(Header)); result.process_bytes(data, data_len + sizeof(Header));
if (crc32 != result.checksum()) { if (crc32 != result.checksum()) {
LOG_ERROR(Input, "UDP Packet CRC check failed. Offset: {}", offsetof(Header, crc)); LOG_ERROR(Input, "UDP Packet CRC check failed. Offset: {}", offsetof(Header, crc));
return {}; return std::nullopt;
} }
return header.type; return header.type;
} }

View File

@ -25,15 +25,15 @@ enum class Type : u32 {
}; };
struct Header { struct Header {
u32_le magic; u32_le magic{};
u16_le protocol_version; u16_le protocol_version{};
u16_le payload_length; u16_le payload_length{};
u32_le crc; u32_le crc{};
u32_le id; u32_le id{};
///> In the protocol, the type of the packet is not part of the header, but its convenient to ///> In the protocol, the type of the packet is not part of the header, but its convenient to
///> include in the header so the callee doesn't have to duplicate the type twice when building ///> include in the header so the callee doesn't have to duplicate the type twice when building
///> the data ///> the data
Type type; Type type{};
}; };
static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size"); static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size");
static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable"); static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable");
@ -44,7 +44,7 @@ constexpr MacAddress EMPTY_MAC_ADDRESS = {0, 0, 0, 0, 0, 0};
#pragma pack(push, 1) #pragma pack(push, 1)
template <typename T> template <typename T>
struct Message { struct Message {
Header header; Header header{};
T data; T data;
}; };
#pragma pack(pop) #pragma pack(pop)
@ -63,7 +63,7 @@ struct Version {};
*/ */
constexpr u32 MAX_PORTS = 4; constexpr u32 MAX_PORTS = 4;
struct PortInfo { struct PortInfo {
u32_le pad_count; ///> Number of ports to request data for u32_le pad_count{}; ///> Number of ports to request data for
std::array<u8, MAX_PORTS> port; std::array<u8, MAX_PORTS> port;
}; };
static_assert(std::is_trivially_copyable_v<PortInfo>, static_assert(std::is_trivially_copyable_v<PortInfo>,
@ -81,9 +81,9 @@ struct PadData {
Mac, Mac,
}; };
/// Determines which method will be used as a look up for the controller /// Determines which method will be used as a look up for the controller
Flags flags; Flags flags{};
/// Index of the port of the controller to retrieve data about /// Index of the port of the controller to retrieve data about
u8 port_id; u8 port_id{};
/// Mac address of the controller to retrieve data about /// Mac address of the controller to retrieve data about
MacAddress mac; MacAddress mac;
}; };
@ -112,20 +112,20 @@ Message<T> Create(const T data, const u32 client_id = 0) {
namespace Response { namespace Response {
struct Version { struct Version {
u16_le version; u16_le version{};
}; };
static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size"); static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size");
static_assert(std::is_trivially_copyable_v<Version>, static_assert(std::is_trivially_copyable_v<Version>,
"UDP Response Version is not trivially copyable"); "UDP Response Version is not trivially copyable");
struct PortInfo { struct PortInfo {
u8 id; u8 id{};
u8 state; u8 state{};
u8 model; u8 model{};
u8 connection_type; u8 connection_type{};
MacAddress mac; MacAddress mac;
u8 battery; u8 battery{};
u8 is_pad_active; u8 is_pad_active{};
}; };
static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size"); static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
static_assert(std::is_trivially_copyable_v<PortInfo>, static_assert(std::is_trivially_copyable_v<PortInfo>,
@ -133,10 +133,10 @@ static_assert(std::is_trivially_copyable_v<PortInfo>,
#pragma pack(push, 1) #pragma pack(push, 1)
struct PadData { struct PadData {
PortInfo info; PortInfo info{};
u32_le packet_counter; u32_le packet_counter{};
u16_le digital_button; u16_le digital_button{};
// The following union isn't trivially copyable but we don't use this input anyway. // The following union isn't trivially copyable but we don't use this input anyway.
// union DigitalButton { // union DigitalButton {
// u16_le button; // u16_le button;
@ -160,46 +160,46 @@ struct PadData {
u8 home; u8 home;
/// If the device supports a "click" on the touchpad, this will change to 1 when a click happens /// If the device supports a "click" on the touchpad, this will change to 1 when a click happens
u8 touch_hard_press; u8 touch_hard_press{};
u8 left_stick_x; u8 left_stick_x{};
u8 left_stick_y; u8 left_stick_y{};
u8 right_stick_x; u8 right_stick_x{};
u8 right_stick_y; u8 right_stick_y{};
struct AnalogButton { struct AnalogButton {
u8 button_8; u8 button_8{};
u8 button_7; u8 button_7{};
u8 button_6; u8 button_6{};
u8 button_5; u8 button_5{};
u8 button_12; u8 button_12{};
u8 button_11; u8 button_11{};
u8 button_10; u8 button_10{};
u8 button_9; u8 button_9{};
u8 button_16; u8 button_16{};
u8 button_15; u8 button_15{};
u8 button_14; u8 button_14{};
u8 button_13; u8 button_13{};
} analog_button; } analog_button;
struct TouchPad { struct TouchPad {
u8 is_active; u8 is_active{};
u8 id; u8 id{};
u16_le x; u16_le x{};
u16_le y; u16_le y{};
} touch_1, touch_2; } touch_1, touch_2;
u64_le motion_timestamp; u64_le motion_timestamp;
struct Accelerometer { struct Accelerometer {
float x; float x{};
float y; float y{};
float z; float z{};
} accel; } accel;
struct Gyroscope { struct Gyroscope {
float pitch; float pitch{};
float yaw; float yaw{};
float roll; float roll{};
} gyro; } gyro;
}; };
#pragma pack(pop) #pragma pack(pop)
@ -211,6 +211,13 @@ static_assert(std::is_trivially_copyable_v<PadData>,
static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE, static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
"UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>"); "UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>");
static_assert(sizeof(PadData::AnalogButton) == 12,
"UDP Response AnalogButton struct has wrong size ");
static_assert(sizeof(PadData::TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
static_assert(sizeof(PadData::Accelerometer) == 12,
"UDP Response Accelerometer struct has wrong size ");
static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");
/** /**
* Create a Response Message from the data * Create a Response Message from the data
* @param data array of bytes sent from the server * @param data array of bytes sent from the server

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <mutex> #include <mutex>
#include <optional>
#include <tuple> #include <tuple>
#include "common/param_package.h" #include "common/param_package.h"
#include "core/frontend/input.h" #include "core/frontend/input.h"
@ -43,7 +44,7 @@ public:
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override { std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override {
{ {
std::lock_guard guard(status->update_mutex); std::lock_guard guard(status->update_mutex);
status->touch_calibration.emplace(); status->touch_calibration = DeviceStatus::CalibrationData{};
// These default values work well for DS4 but probably not other touch inputs // 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_x = params.Get("min_x", 100);
status->touch_calibration->min_y = params.Get("min_y", 50); status->touch_calibration->min_y = params.Get("min_y", 50);