diff options
Diffstat (limited to 'src/input_common')
-rwxr-xr-x | src/input_common/analog_from_button.cpp | 18 | ||||
-rw-r--r-- | src/input_common/gcadapter/gc_poller.cpp | 10 | ||||
-rw-r--r-- | src/input_common/keyboard.cpp | 31 | ||||
-rw-r--r-- | src/input_common/mouse/mouse_input.cpp | 95 | ||||
-rw-r--r-- | src/input_common/mouse/mouse_input.h | 24 | ||||
-rw-r--r-- | src/input_common/mouse/mouse_poller.cpp | 33 | ||||
-rw-r--r-- | src/input_common/sdl/sdl_impl.cpp | 18 | ||||
-rw-r--r-- | src/input_common/settings.h | 1 | ||||
-rw-r--r-- | src/input_common/touch_from_button.cpp | 15 | ||||
-rw-r--r-- | src/input_common/udp/client.cpp | 148 | ||||
-rw-r--r-- | src/input_common/udp/client.h | 33 | ||||
-rw-r--r-- | src/input_common/udp/protocol.h | 16 | ||||
-rw-r--r-- | src/input_common/udp/udp.cpp | 36 |
13 files changed, 344 insertions, 134 deletions
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index 40b516f85..770893687 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <atomic> #include <chrono> #include <cmath> #include <thread> @@ -20,13 +21,16 @@ public: : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), modifier_angle(modifier_angle_) { + update_thread_running.store(true); update_thread = std::thread(&Analog::UpdateStatus, this); } ~Analog() override { - update_thread_running = false; - if (update_thread.joinable()) { - update_thread.join(); + if (update_thread_running.load()) { + update_thread_running.store(false); + if (update_thread.joinable()) { + update_thread.join(); + } } } @@ -58,7 +62,7 @@ public: } void UpdateStatus() { - while (update_thread_running) { + while (update_thread_running.load()) { const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; bool r = right->GetStatus(); @@ -135,6 +139,10 @@ public: static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF)); } + Input::AnalogProperties GetAnalogProperties() const override { + return {modifier_scale, 1.0f, 0.5f}; + } + bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { switch (direction) { case Input::AnalogDirection::RIGHT: @@ -160,7 +168,7 @@ private: float angle{}; float amplitude{}; std::thread update_thread; - bool update_thread_running{true}; + std::atomic<bool> update_thread_running{}; }; std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 9670bdeb2..1b6ded8d6 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -185,6 +185,16 @@ public: return {0.0f, 0.0f}; } + std::tuple<float, float> GetRawStatus() const override { + const float x = GetAxis(axis_x); + const float y = GetAxis(axis_y); + return {x, y}; + } + + Input::AnalogProperties GetAnalogProperties() const override { + return {deadzone, range, 0.5f}; + } + bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { const auto [x, y] = GetStatus(); const float directional_deadzone = 0.5f; diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp index 24a6f7a33..c467ff4c5 100644 --- a/src/input_common/keyboard.cpp +++ b/src/input_common/keyboard.cpp @@ -12,20 +12,39 @@ namespace InputCommon { class KeyButton final : public Input::ButtonDevice { public: - explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_) - : key_button_list(std::move(key_button_list_)) {} + explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_, bool toggle_) + : key_button_list(std::move(key_button_list_)), toggle(toggle_) {} ~KeyButton() override; bool GetStatus() const override { + if (toggle) { + return toggled_status.load(std::memory_order_relaxed); + } return status.load(); } + void ToggleButton() { + if (lock) { + return; + } + lock = true; + const bool old_toggle_status = toggled_status.load(); + toggled_status.store(!old_toggle_status); + } + + void UnlockButton() { + lock = false; + } + friend class KeyButtonList; private: std::shared_ptr<KeyButtonList> key_button_list; std::atomic<bool> status{false}; + std::atomic<bool> toggled_status{false}; + bool lock{false}; + const bool toggle; }; struct KeyButtonPair { @@ -51,6 +70,11 @@ public: for (const KeyButtonPair& pair : list) { if (pair.key_code == key_code) { pair.key_button->status.store(pressed); + if (pressed) { + pair.key_button->ToggleButton(); + } else { + pair.key_button->UnlockButton(); + } } } } @@ -75,7 +99,8 @@ KeyButton::~KeyButton() { std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) { const int key_code = params.Get("code", 0); - std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list); + const bool toggle = params.Get("toggle", false); + std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list, toggle); key_button_list->AddKeyButton(key_code, button.get()); return button; } diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp index 10786a541..329e416c7 100644 --- a/src/input_common/mouse/mouse_input.cpp +++ b/src/input_common/mouse/mouse_input.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "core/settings.h" #include "input_common/mouse/mouse_input.h" namespace MouseInput { @@ -32,10 +33,18 @@ void Mouse::UpdateThread() { info.motion.UpdateOrientation(update_time * 1000); info.tilt_speed = 0; info.data.motion = info.motion.GetMotion(); + if (Settings::values.mouse_panning) { + info.last_mouse_change *= 0.96f; + info.data.axis = {static_cast<int>(16 * info.last_mouse_change.x), + static_cast<int>(16 * -info.last_mouse_change.y)}; + } } if (configuring) { UpdateYuzuSettings(); } + if (mouse_panning_timout++ > 20) { + StopPanning(); + } std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); } } @@ -50,7 +59,7 @@ void Mouse::UpdateYuzuSettings() { }); } -void Mouse::PressButton(int x, int y, int button_) { +void Mouse::PressButton(int x, int y, MouseButton button_) { const auto button_index = static_cast<std::size_t>(button_); if (button_index >= mouse_info.size()) { return; @@ -58,15 +67,52 @@ void Mouse::PressButton(int x, int y, int button_) { const auto button = 1U << button_index; buttons |= static_cast<u16>(button); - last_button = static_cast<MouseButton>(button_index); + last_button = button_; mouse_info[button_index].mouse_origin = Common::MakeVec(x, y); mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y); mouse_info[button_index].data.pressed = true; } -void Mouse::MouseMove(int x, int y) { +void Mouse::StopPanning() { for (MouseInfo& info : mouse_info) { + if (Settings::values.mouse_panning) { + info.data.axis = {}; + info.tilt_speed = 0; + info.last_mouse_change = {}; + } + } +} + +void Mouse::MouseMove(int x, int y, int center_x, int center_y) { + for (MouseInfo& info : mouse_info) { + if (Settings::values.mouse_panning) { + auto mouse_change = + (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); + mouse_panning_timout = 0; + + if (mouse_change.y == 0 && mouse_change.x == 0) { + continue; + } + const auto mouse_change_length = mouse_change.Length(); + if (mouse_change_length < 3.0f) { + mouse_change /= mouse_change_length / 3.0f; + } + + info.last_mouse_change = (info.last_mouse_change * 0.91f) + (mouse_change * 0.09f); + + const auto last_mouse_change_length = info.last_mouse_change.Length(); + if (last_mouse_change_length > 8.0f) { + info.last_mouse_change /= last_mouse_change_length / 8.0f; + } else if (last_mouse_change_length < 1.0f) { + info.last_mouse_change = mouse_change / mouse_change.Length(); + } + + info.tilt_direction = info.last_mouse_change; + info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity; + continue; + } + if (info.data.pressed) { const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin; const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position; @@ -83,7 +129,7 @@ void Mouse::MouseMove(int x, int y) { } } -void Mouse::ReleaseButton(int button_) { +void Mouse::ReleaseButton(MouseButton button_) { const auto button_index = static_cast<std::size_t>(button_); if (button_index >= mouse_info.size()) { return; @@ -106,11 +152,52 @@ void Mouse::BeginConfiguration() { void Mouse::EndConfiguration() { buttons = 0; + for (MouseInfo& info : mouse_info) { + info.tilt_speed = 0; + info.data.pressed = false; + info.data.axis = {0, 0}; + } last_button = MouseButton::Undefined; mouse_queue.Clear(); configuring = false; } +bool Mouse::ToggleButton(std::size_t button_) { + if (button_ >= mouse_info.size()) { + return false; + } + const auto button = 1U << button_; + const bool button_state = (toggle_buttons & button) != 0; + const bool button_lock = (lock_buttons & button) != 0; + + if (button_lock) { + return button_state; + } + + lock_buttons |= static_cast<u16>(button); + + if (button_state) { + toggle_buttons &= static_cast<u16>(0xFF - button); + } else { + toggle_buttons |= static_cast<u16>(button); + } + + return !button_state; +} + +bool Mouse::UnlockButton(std::size_t button_) { + if (button_ >= mouse_info.size()) { + return false; + } + + const auto button = 1U << button_; + const bool button_state = (toggle_buttons & button) != 0; + + lock_buttons &= static_cast<u16>(0xFF - button); + + return button_state; +} + Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() { return mouse_queue; } diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h index 58803c1bf..750d9b011 100644 --- a/src/input_common/mouse/mouse_input.h +++ b/src/input_common/mouse/mouse_input.h @@ -18,10 +18,12 @@ namespace MouseInput { enum class MouseButton { Left, - Wheel, Right, - Forward, + Wheel, Backward, + Forward, + Task, + Extra, Undefined, }; @@ -51,19 +53,24 @@ public: * @param y the y-coordinate of the cursor * @param button_ the button pressed */ - void PressButton(int x, int y, int button_); + void PressButton(int x, int y, MouseButton button_); /** * Signals that mouse has moved. * @param x the x-coordinate of the cursor * @param y the y-coordinate of the cursor + * @param center_x the x-coordinate of the middle of the screen + * @param center_y the y-coordinate of the middle of the screen */ - void MouseMove(int x, int y); + void MouseMove(int x, int y, int center_x, int center_y); /** * Signals that a motion sensor tilt has ended. */ - void ReleaseButton(int button_); + void ReleaseButton(MouseButton button_); + + [[nodiscard]] bool ToggleButton(std::size_t button_); + [[nodiscard]] bool UnlockButton(std::size_t button_); [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue(); [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const; @@ -74,11 +81,13 @@ public: private: void UpdateThread(); void UpdateYuzuSettings(); + void StopPanning(); struct MouseInfo { InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f}; Common::Vec2<int> mouse_origin; Common::Vec2<int> last_mouse_position; + Common::Vec2<float> last_mouse_change; bool is_tilting = false; float sensitivity{0.120f}; @@ -88,11 +97,14 @@ private: }; u16 buttons{}; + u16 toggle_buttons{}; + u16 lock_buttons{}; std::thread update_thread; MouseButton last_button{MouseButton::Undefined}; - std::array<MouseInfo, 5> mouse_info; + std::array<MouseInfo, 7> mouse_info; Common::SPSCQueue<MouseStatus> mouse_queue; bool configuring{false}; bool update_thread_running{true}; + int mouse_panning_timout{}; }; } // namespace MouseInput diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp index 508eb0c7d..0e1db54fb 100644 --- a/src/input_common/mouse/mouse_poller.cpp +++ b/src/input_common/mouse/mouse_poller.cpp @@ -6,6 +6,7 @@ #include <utility> #include "common/threadsafe_queue.h" +#include "core/settings.h" #include "input_common/mouse/mouse_input.h" #include "input_common/mouse/mouse_poller.h" @@ -13,16 +14,25 @@ namespace InputCommon { class MouseButton final : public Input::ButtonDevice { public: - explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_) - : button(button_), mouse_input(mouse_input_) {} + explicit MouseButton(u32 button_, bool toggle_, MouseInput::Mouse* mouse_input_) + : button(button_), toggle(toggle_), mouse_input(mouse_input_) {} bool GetStatus() const override { - return mouse_input->GetMouseState(button).pressed; + const bool button_state = mouse_input->GetMouseState(button).pressed; + if (!toggle) { + return button_state; + } + + if (button_state) { + return mouse_input->ToggleButton(button); + } + return mouse_input->UnlockButton(button); } private: const u32 button; - const MouseInput::Mouse* mouse_input; + const bool toggle; + MouseInput::Mouse* mouse_input; }; MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) @@ -31,8 +41,9 @@ MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_ std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create( const Common::ParamPackage& params) { const auto button_id = params.Get("button", 0); + const auto toggle = params.Get("toggle", false); - return std::make_unique<MouseButton>(button_id, mouse_input.get()); + return std::make_unique<MouseButton>(button_id, toggle, mouse_input.get()); } Common::ParamPackage MouseButtonFactory::GetNextInput() const { @@ -71,7 +82,7 @@ public: std::lock_guard lock{mutex}; const auto axis_value = static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis)); - return axis_value / (100.0f * range); + return axis_value * Settings::values.mouse_panning_sensitivity / (100.0f * range); } std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { @@ -106,6 +117,16 @@ public: return {0.0f, 0.0f}; } + std::tuple<float, float> GetRawStatus() const override { + const float x = GetAxis(axis_x); + const float y = GetAxis(axis_y); + return {x, y}; + } + + Input::AnalogProperties GetAnalogProperties() const override { + return {deadzone, range, 0.5f}; + } + private: const u32 button; const u32 axis_x; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d32eb732a..f67de37e3 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -81,10 +81,14 @@ public: } bool RumblePlay(u16 amp_low, u16 amp_high) { + constexpr u32 rumble_max_duration_ms = 1000; + if (sdl_controller) { - return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; + return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, + rumble_max_duration_ms) == 0; } else if (sdl_joystick) { - return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; + return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, + rumble_max_duration_ms) == 0; } return false; @@ -373,6 +377,16 @@ public: return {}; } + std::tuple<float, float> GetRawStatus() const override { + const float x = joystick->GetAxis(axis_x, range); + const float y = joystick->GetAxis(axis_y, range); + return {x, -y}; + } + + Input::AnalogProperties GetAnalogProperties() const override { + return {deadzone, range, 0.5f}; + } + bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { const auto [x, y] = GetStatus(); const float directional_deadzone = 0.5f; diff --git a/src/input_common/settings.h b/src/input_common/settings.h index 75486554b..a59f5d461 100644 --- a/src/input_common/settings.h +++ b/src/input_common/settings.h @@ -340,6 +340,7 @@ enum class ControllerType { LeftJoycon, RightJoycon, Handheld, + GameCube, }; struct PlayerInput { diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index a07124a86..ffbe4f2ed 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -25,18 +25,19 @@ public: } } - std::tuple<float, float, bool> GetStatus() const override { - for (const auto& m : map) { - const bool state = std::get<0>(m)->GetStatus(); + Input::TouchStatus GetStatus() const override { + Input::TouchStatus touch_status{}; + for (std::size_t id = 0; id < map.size() && id < touch_status.size(); ++id) { + const bool state = std::get<0>(map[id])->GetStatus(); if (state) { - const float x = static_cast<float>(std::get<1>(m)) / + const float x = static_cast<float>(std::get<1>(map[id])) / static_cast<int>(Layout::ScreenUndocked::Width); - const float y = static_cast<float>(std::get<2>(m)) / + const float y = static_cast<float>(std::get<2>(map[id])) / static_cast<int>(Layout::ScreenUndocked::Height); - return {x, y, true}; + touch_status[id] = {x, y, true}; } } - return {}; + return touch_status; } private: diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 412d57896..df73f9ff7 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -5,6 +5,7 @@ #include <chrono> #include <cstring> #include <functional> +#include <random> #include <thread> #include <boost/asio.hpp> #include "common/logging/log.h" @@ -26,10 +27,10 @@ class Socket { public: using clock = std::chrono::system_clock; - explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_, + explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, SocketCallback callback_) : 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(GenerateRandomClientId()), pad_index(pad_index_) { boost::system::error_code ec{}; auto ipv4 = boost::asio::ip::make_address_v4(host, ec); @@ -63,6 +64,11 @@ public: } private: + u32 GenerateRandomClientId() const { + std::random_device device; + return device(); + } + void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) { if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { switch (*type) { @@ -115,7 +121,7 @@ private: boost::asio::basic_waitable_timer<clock> timer; udp::socket socket; - u32 client_id{}; + const u32 client_id; std::size_t pad_index{}; static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); @@ -136,6 +142,7 @@ static void SocketLoop(Socket* socket) { Client::Client() { LOG_INFO(Input, "Udp Initialization started"); + finger_id.fill(MAX_TOUCH_FINGERS); ReloadSockets(); } @@ -143,6 +150,10 @@ Client::~Client() { Reset(); } +Client::ClientData::ClientData() = default; + +Client::ClientData::~ClientData() = default; + std::vector<Common::ParamPackage> Client::GetInputDevices() const { std::vector<Common::ParamPackage> devices; for (std::size_t client = 0; client < clients.size(); client++) { @@ -176,7 +187,7 @@ void Client::ReloadSockets() { std::string server_token; std::size_t client = 0; while (std::getline(servers_ss, server_token, ',')) { - if (client == max_udp_clients) { + if (client == MAX_UDP_CLIENTS) { break; } std::stringstream server_ss(server_token); @@ -194,11 +205,11 @@ void Client::ReloadSockets() { for (std::size_t pad = 0; pad < 4; ++pad) { const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port, pad); - if (client_number != max_udp_clients) { + if (client_number != MAX_UDP_CLIENTS) { LOG_ERROR(Input, "Duplicated UDP servers found"); continue; } - StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872); + StartCommunication(client++, udp_input_address, udp_input_port, pad); } } } @@ -213,7 +224,7 @@ std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t return client; } } - return max_udp_clients; + return MAX_UDP_CLIENTS; } void Client::OnVersion([[maybe_unused]] Response::Version data) { @@ -259,39 +270,20 @@ void Client::OnPadData(Response::PadData data, std::size_t client) { std::lock_guard guard(clients[client].status.update_mutex); clients[client].status.motion_status = clients[client].motion.GetMotion(); - // 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. - const bool is_active = data.touch_1.is_active != 0; - - float x = 0; - float y = 0; - - 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 = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - - min_x) / - static_cast<float>(max_x - min_x); - y = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) - - min_y) / - static_cast<float>(max_y - min_y); + for (std::size_t id = 0; id < data.touch.size(); ++id) { + UpdateTouchInput(data.touch[id], client, id); } - clients[client].status.touch_status = {x, y, is_active}; - if (configuring) { const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope(); const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration(); - UpdateYuzuSettings(client, accelerometer, gyroscope, is_active); + UpdateYuzuSettings(client, accelerometer, gyroscope); } } } void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, - std::size_t pad_index, u32 client_id) { + std::size_t pad_index) { SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, [this](Response::PortInfo info) { OnPortInfo(info); }, [this, client](Response::PadData data) { OnPadData(data, client); }}; @@ -301,7 +293,7 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16 clients[client].port = port; clients[client].pad_index = pad_index; clients[client].active = 0; - clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); + clients[client].socket = std::make_unique<Socket>(host, port, pad_index, callback); clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; // Set motion parameters // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode @@ -320,21 +312,17 @@ void Client::Reset() { } void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, - const Common::Vec3<float>& gyro, bool touch) { + const Common::Vec3<float>& gyro) { if (gyro.Length() > 0.2f) { - LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}", - client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch); + 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 = clients[client].pad_index, }; - if (touch) { - pad.touch = PadTouch::Click; - pad_queue.Push(pad); - } - for (size_t i = 0; i < 3; ++i) { + for (std::size_t i = 0; i < 3; ++i) { if (gyro[i] > 5.0f || gyro[i] < -5.0f) { pad.motion = static_cast<PadMotion>(i); pad.motion_value = gyro[i]; @@ -348,6 +336,50 @@ void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& a } } +std::optional<std::size_t> 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); + const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100)); + const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50)); + const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800)); + const u16 max_y = static_cast<u16>(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<float>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) / + static_cast<float>(max_x - min_x); + y = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) / + static_cast<float>(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; @@ -360,7 +392,7 @@ void Client::EndConfiguration() { DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) { const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == max_udp_clients) { + if (client_number == MAX_UDP_CLIENTS) { return clients[0].status; } return clients[client_number].status; @@ -368,12 +400,20 @@ DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const { const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == max_udp_clients) { + if (client_number == MAX_UDP_CLIENTS) { return clients[0].status; } return clients[client_number].status; } +Input::TouchStatus& Client::GetTouchState() { + return touch_status; +} + +const Input::TouchStatus& Client::GetTouchState() const { + return touch_status; +} + Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() { return pad_queue; } @@ -382,7 +422,7 @@ const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const { return pad_queue; } -void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, +void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, const std::function<void()>& success_callback, const std::function<void()>& failure_callback) { std::thread([=] { @@ -392,7 +432,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, .port_info = [](Response::PortInfo) {}, .pad_data = [&](Response::PadData) { success_event.Set(); }, }; - Socket socket{host, port, pad_index, client_id, std::move(callback)}; + Socket socket{host, port, pad_index, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; const bool result = success_event.WaitFor(std::chrono::seconds(5)); socket.Stop(); @@ -406,7 +446,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, } CalibrationConfigurationJob::CalibrationConfigurationJob( - const std::string& host, u16 port, std::size_t pad_index, u32 client_id, + const std::string& host, u16 port, std::size_t pad_index, std::function<void(Status)> status_callback, std::function<void(u16, u16, u16, u16)> data_callback) { @@ -426,24 +466,24 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( current_status = Status::Ready; status_callback(current_status); } - if (data.touch_1.is_active == 0) { + if (data.touch[0].is_active == 0) { return; } - LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, - data.touch_1.y); - min_x = std::min(min_x, static_cast<u16>(data.touch_1.x)); - min_y = std::min(min_y, static_cast<u16>(data.touch_1.y)); + LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, + data.touch[0].y); + min_x = std::min(min_x, static_cast<u16>(data.touch[0].x)); + min_y = std::min(min_y, static_cast<u16>(data.touch[0].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) { + if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && + data.touch[0].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; + max_x = data.touch[0].x; + max_y = data.touch[0].y; current_status = Status::Completed; data_callback(min_x, min_y, max_x, max_y); status_callback(current_status); @@ -451,7 +491,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( complete_event.Set(); } }}; - Socket socket{host, port, pad_index, client_id, std::move(callback)}; + Socket socket{host, port, pad_index, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; complete_event.Wait(); socket.Stop(); diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 00c8b09f5..e9e438e88 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -28,6 +28,7 @@ class Socket; namespace Response { struct PadData; struct PortInfo; +struct TouchPad; struct Version; } // namespace Response @@ -50,7 +51,6 @@ struct UDPPadStatus { std::string host{"127.0.0.1"}; u16 port{26760}; std::size_t pad_index{}; - PadTouch touch{PadTouch::Undefined}; PadMotion motion{PadMotion::Undefined}; f32 motion_value{0.0f}; }; @@ -93,8 +93,14 @@ public: 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 ClientData { + ClientData(); + ~ClientData(); + std::string host{"127.0.0.1"}; u16 port{26760}; std::size_t pad_index{}; @@ -120,16 +126,27 @@ 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, - std::size_t pad_index, u32 client_id); + std::size_t pad_index); void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, - const Common::Vec3<float>& gyro, bool touch); + const Common::Vec3<float>& gyro); + + // Returns an unused finger id, if there is no fingers available std::nullopt will be + // returned + std::optional<std::size_t> 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; // Allocate clients for 8 udp servers - const std::size_t max_udp_clients = 32; - std::array<ClientData, 4 * 8> clients; - Common::SPSCQueue<UDPPadStatus> pad_queue; + static constexpr std::size_t MAX_UDP_CLIENTS = 4 * 8; + // Each client can have up 2 touch inputs + static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2; + std::array<ClientData, MAX_UDP_CLIENTS> clients{}; + Common::SPSCQueue<UDPPadStatus> pad_queue{}; + Input::TouchStatus touch_status{}; + std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{}; }; /// An async job allowing configuration of the touchpad calibration. @@ -148,7 +165,7 @@ public: * @param data_callback Called when calibration data is ready */ explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index, - u32 client_id, std::function<void(Status)> status_callback, + std::function<void(Status)> status_callback, std::function<void(u16, u16, u16, u16)> data_callback); ~CalibrationConfigurationJob(); void Stop(); @@ -157,7 +174,7 @@ private: Common::Event complete_event; }; -void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id, +void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, const std::function<void()>& success_callback, const std::function<void()>& failure_callback); diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index fc1aea4b9..a3d276697 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h @@ -140,6 +140,14 @@ static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong si static_assert(std::is_trivially_copyable_v<PortInfo>, "UDP Response PortInfo is not trivially copyable"); +struct TouchPad { + u8 is_active{}; + u8 id{}; + u16_le x{}; + u16_le y{}; +}; +static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size "); + #pragma pack(push, 1) struct PadData { PortInfo info{}; @@ -190,12 +198,7 @@ struct PadData { u8 button_13{}; } analog_button; - struct TouchPad { - u8 is_active{}; - u8 id{}; - u16_le x{}; - u16_le y{}; - } touch_1, touch_2; + std::array<TouchPad, 2> touch; u64_le motion_timestamp; @@ -222,7 +225,6 @@ static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE, 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 "); diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index c5da27a38..9829da6f0 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -78,14 +78,14 @@ public: explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_) : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} - std::tuple<float, float, bool> GetStatus() const override { - return client->GetPadState(ip, port, pad).touch_status; + Input::TouchStatus GetStatus() const override { + return client->GetTouchState(); } private: const std::string ip; - const u16 port; - const u16 pad; + [[maybe_unused]] const u16 port; + [[maybe_unused]] const u16 pad; CemuhookUDP::Client* client; mutable std::mutex mutex; }; @@ -107,32 +107,4 @@ std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamP return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get()); } -void UDPTouchFactory::BeginConfiguration() { - polling = true; - client->BeginConfiguration(); -} - -void UDPTouchFactory::EndConfiguration() { - polling = false; - client->EndConfiguration(); -} - -Common::ParamPackage UDPTouchFactory::GetNextInput() { - Common::ParamPackage params; - CemuhookUDP::UDPPadStatus pad; - auto& queue = client->GetPadQueue(); - while (queue.Pop(pad)) { - if (pad.touch == CemuhookUDP::PadTouch::Undefined) { - continue; - } - params.Set("engine", "cemuhookudp"); - params.Set("ip", pad.host); - params.Set("port", static_cast<u16>(pad.port)); - params.Set("pad_index", static_cast<u16>(pad.pad_index)); - params.Set("touch", static_cast<u16>(pad.touch)); - return params; - } - return params; -} - } // namespace InputCommon |