summaryrefslogtreecommitdiff
path: root/src/input_common
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/CMakeLists.txt10
-rw-r--r--src/input_common/main.cpp75
-rw-r--r--src/input_common/main.h41
-rw-r--r--src/input_common/motion_emu.cpp179
-rw-r--r--src/input_common/motion_emu.h46
-rw-r--r--src/input_common/mouse/mouse_input.cpp127
-rw-r--r--src/input_common/mouse/mouse_input.h99
-rw-r--r--src/input_common/mouse/mouse_poller.cpp261
-rw-r--r--src/input_common/mouse/mouse_poller.h109
-rw-r--r--src/input_common/udp/client.cpp143
-rw-r--r--src/input_common/udp/client.h40
-rw-r--r--src/input_common/udp/udp.cpp64
12 files changed, 850 insertions, 344 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 1d1b2e08a..38ab31898 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -5,8 +5,6 @@ add_library(input_common STATIC
5 keyboard.h 5 keyboard.h
6 main.cpp 6 main.cpp
7 main.h 7 main.h
8 motion_emu.cpp
9 motion_emu.h
10 motion_from_button.cpp 8 motion_from_button.cpp
11 motion_from_button.h 9 motion_from_button.h
12 motion_input.cpp 10 motion_input.cpp
@@ -19,6 +17,10 @@ add_library(input_common STATIC
19 gcadapter/gc_adapter.h 17 gcadapter/gc_adapter.h
20 gcadapter/gc_poller.cpp 18 gcadapter/gc_poller.cpp
21 gcadapter/gc_poller.h 19 gcadapter/gc_poller.h
20 mouse/mouse_input.cpp
21 mouse/mouse_input.h
22 mouse/mouse_poller.cpp
23 mouse/mouse_poller.h
22 sdl/sdl.cpp 24 sdl/sdl.cpp
23 sdl/sdl.h 25 sdl/sdl.h
24 udp/client.cpp 26 udp/client.cpp
@@ -56,8 +58,8 @@ else()
56 -Werror=reorder 58 -Werror=reorder
57 -Werror=shadow 59 -Werror=shadow
58 -Werror=sign-compare 60 -Werror=sign-compare
59 -Werror=unused-but-set-parameter 61 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
60 -Werror=unused-but-set-variable 62 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
61 -Werror=unused-variable 63 -Werror=unused-variable
62 ) 64 )
63endif() 65endif()
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index e59ad4ff5..7c4e7dd3b 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -10,8 +10,9 @@
10#include "input_common/gcadapter/gc_poller.h" 10#include "input_common/gcadapter/gc_poller.h"
11#include "input_common/keyboard.h" 11#include "input_common/keyboard.h"
12#include "input_common/main.h" 12#include "input_common/main.h"
13#include "input_common/motion_emu.h"
14#include "input_common/motion_from_button.h" 13#include "input_common/motion_from_button.h"
14#include "input_common/mouse/mouse_input.h"
15#include "input_common/mouse/mouse_poller.h"
15#include "input_common/touch_from_button.h" 16#include "input_common/touch_from_button.h"
16#include "input_common/udp/client.h" 17#include "input_common/udp/client.h"
17#include "input_common/udp/udp.h" 18#include "input_common/udp/udp.h"
@@ -37,8 +38,6 @@ struct InputSubsystem::Impl {
37 std::make_shared<AnalogFromButton>()); 38 std::make_shared<AnalogFromButton>());
38 Input::RegisterFactory<Input::MotionDevice>("keyboard", 39 Input::RegisterFactory<Input::MotionDevice>("keyboard",
39 std::make_shared<MotionFromButton>()); 40 std::make_shared<MotionFromButton>());
40 motion_emu = std::make_shared<MotionEmu>();
41 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
42 Input::RegisterFactory<Input::TouchDevice>("touch_from_button", 41 Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
43 std::make_shared<TouchFromButtonFactory>()); 42 std::make_shared<TouchFromButtonFactory>());
44 43
@@ -51,6 +50,16 @@ struct InputSubsystem::Impl {
51 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion); 50 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
52 udptouch = std::make_shared<UDPTouchFactory>(udp); 51 udptouch = std::make_shared<UDPTouchFactory>(udp);
53 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch); 52 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
53
54 mouse = std::make_shared<MouseInput::Mouse>();
55 mousebuttons = std::make_shared<MouseButtonFactory>(mouse);
56 Input::RegisterFactory<Input::ButtonDevice>("mouse", mousebuttons);
57 mouseanalog = std::make_shared<MouseAnalogFactory>(mouse);
58 Input::RegisterFactory<Input::AnalogDevice>("mouse", mouseanalog);
59 mousemotion = std::make_shared<MouseMotionFactory>(mouse);
60 Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
61 mousetouch = std::make_shared<MouseTouchFactory>(mouse);
62 Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
54 } 63 }
55 64
56 void Shutdown() { 65 void Shutdown() {
@@ -58,8 +67,6 @@ struct InputSubsystem::Impl {
58 Input::UnregisterFactory<Input::MotionDevice>("keyboard"); 67 Input::UnregisterFactory<Input::MotionDevice>("keyboard");
59 keyboard.reset(); 68 keyboard.reset();
60 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); 69 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
61 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
62 motion_emu.reset();
63 Input::UnregisterFactory<Input::TouchDevice>("touch_from_button"); 70 Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
64#ifdef HAVE_SDL2 71#ifdef HAVE_SDL2
65 sdl.reset(); 72 sdl.reset();
@@ -77,6 +84,16 @@ struct InputSubsystem::Impl {
77 84
78 udpmotion.reset(); 85 udpmotion.reset();
79 udptouch.reset(); 86 udptouch.reset();
87
88 Input::UnregisterFactory<Input::ButtonDevice>("mouse");
89 Input::UnregisterFactory<Input::AnalogDevice>("mouse");
90 Input::UnregisterFactory<Input::MotionDevice>("mouse");
91 Input::UnregisterFactory<Input::TouchDevice>("mouse");
92
93 mousebuttons.reset();
94 mouseanalog.reset();
95 mousemotion.reset();
96 mousetouch.reset();
80 } 97 }
81 98
82 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 99 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
@@ -140,7 +157,6 @@ struct InputSubsystem::Impl {
140 } 157 }
141 158
142 std::shared_ptr<Keyboard> keyboard; 159 std::shared_ptr<Keyboard> keyboard;
143 std::shared_ptr<MotionEmu> motion_emu;
144#ifdef HAVE_SDL2 160#ifdef HAVE_SDL2
145 std::unique_ptr<SDL::State> sdl; 161 std::unique_ptr<SDL::State> sdl;
146#endif 162#endif
@@ -149,8 +165,13 @@ struct InputSubsystem::Impl {
149 std::shared_ptr<GCVibrationFactory> gcvibration; 165 std::shared_ptr<GCVibrationFactory> gcvibration;
150 std::shared_ptr<UDPMotionFactory> udpmotion; 166 std::shared_ptr<UDPMotionFactory> udpmotion;
151 std::shared_ptr<UDPTouchFactory> udptouch; 167 std::shared_ptr<UDPTouchFactory> udptouch;
168 std::shared_ptr<MouseButtonFactory> mousebuttons;
169 std::shared_ptr<MouseAnalogFactory> mouseanalog;
170 std::shared_ptr<MouseMotionFactory> mousemotion;
171 std::shared_ptr<MouseTouchFactory> mousetouch;
152 std::shared_ptr<CemuhookUDP::Client> udp; 172 std::shared_ptr<CemuhookUDP::Client> udp;
153 std::shared_ptr<GCAdapter::Adapter> gcadapter; 173 std::shared_ptr<GCAdapter::Adapter> gcadapter;
174 std::shared_ptr<MouseInput::Mouse> mouse;
154}; 175};
155 176
156InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} 177InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -173,12 +194,12 @@ const Keyboard* InputSubsystem::GetKeyboard() const {
173 return impl->keyboard.get(); 194 return impl->keyboard.get();
174} 195}
175 196
176MotionEmu* InputSubsystem::GetMotionEmu() { 197MouseInput::Mouse* InputSubsystem::GetMouse() {
177 return impl->motion_emu.get(); 198 return impl->mouse.get();
178} 199}
179 200
180const MotionEmu* InputSubsystem::GetMotionEmu() const { 201const MouseInput::Mouse* InputSubsystem::GetMouse() const {
181 return impl->motion_emu.get(); 202 return impl->mouse.get();
182} 203}
183 204
184std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 205std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
@@ -229,11 +250,43 @@ const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
229 return impl->udptouch.get(); 250 return impl->udptouch.get();
230} 251}
231 252
253MouseButtonFactory* InputSubsystem::GetMouseButtons() {
254 return impl->mousebuttons.get();
255}
256
257const MouseButtonFactory* InputSubsystem::GetMouseButtons() const {
258 return impl->mousebuttons.get();
259}
260
261MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() {
262 return impl->mouseanalog.get();
263}
264
265const MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() const {
266 return impl->mouseanalog.get();
267}
268
269MouseMotionFactory* InputSubsystem::GetMouseMotions() {
270 return impl->mousemotion.get();
271}
272
273const MouseMotionFactory* InputSubsystem::GetMouseMotions() const {
274 return impl->mousemotion.get();
275}
276
277MouseTouchFactory* InputSubsystem::GetMouseTouch() {
278 return impl->mousetouch.get();
279}
280
281const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
282 return impl->mousetouch.get();
283}
284
232void InputSubsystem::ReloadInputDevices() { 285void InputSubsystem::ReloadInputDevices() {
233 if (!impl->udp) { 286 if (!impl->udp) {
234 return; 287 return;
235 } 288 }
236 impl->udp->ReloadUDPClient(); 289 impl->udp->ReloadSockets();
237} 290}
238 291
239std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( 292std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
diff --git a/src/input_common/main.h b/src/input_common/main.h
index dded3f1ef..5d6f26385 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -25,6 +25,10 @@ namespace Settings::NativeMotion {
25enum Values : int; 25enum Values : int;
26} 26}
27 27
28namespace MouseInput {
29class Mouse;
30}
31
28namespace InputCommon { 32namespace InputCommon {
29namespace Polling { 33namespace Polling {
30 34
@@ -56,8 +60,11 @@ class GCAnalogFactory;
56class GCButtonFactory; 60class GCButtonFactory;
57class UDPMotionFactory; 61class UDPMotionFactory;
58class UDPTouchFactory; 62class UDPTouchFactory;
63class MouseButtonFactory;
64class MouseAnalogFactory;
65class MouseMotionFactory;
66class MouseTouchFactory;
59class Keyboard; 67class Keyboard;
60class MotionEmu;
61 68
62/** 69/**
63 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default 70 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
@@ -90,11 +97,11 @@ public:
90 /// Retrieves the underlying keyboard device. 97 /// Retrieves the underlying keyboard device.
91 [[nodiscard]] const Keyboard* GetKeyboard() const; 98 [[nodiscard]] const Keyboard* GetKeyboard() const;
92 99
93 /// Retrieves the underlying motion emulation factory. 100 /// Retrieves the underlying mouse device.
94 [[nodiscard]] MotionEmu* GetMotionEmu(); 101 [[nodiscard]] MouseInput::Mouse* GetMouse();
95 102
96 /// Retrieves the underlying motion emulation factory. 103 /// Retrieves the underlying mouse device.
97 [[nodiscard]] const MotionEmu* GetMotionEmu() const; 104 [[nodiscard]] const MouseInput::Mouse* GetMouse() const;
98 105
99 /** 106 /**
100 * Returns all available input devices that this Factory can create a new device with. 107 * Returns all available input devices that this Factory can create a new device with.
@@ -137,6 +144,30 @@ public:
137 /// Retrieves the underlying udp touch handler. 144 /// Retrieves the underlying udp touch handler.
138 [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const; 145 [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
139 146
147 /// Retrieves the underlying GameCube button handler.
148 [[nodiscard]] MouseButtonFactory* GetMouseButtons();
149
150 /// Retrieves the underlying GameCube button handler.
151 [[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
152
153 /// Retrieves the underlying udp touch handler.
154 [[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
155
156 /// Retrieves the underlying udp touch handler.
157 [[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
158
159 /// Retrieves the underlying udp motion handler.
160 [[nodiscard]] MouseMotionFactory* GetMouseMotions();
161
162 /// Retrieves the underlying udp motion handler.
163 [[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
164
165 /// Retrieves the underlying udp touch handler.
166 [[nodiscard]] MouseTouchFactory* GetMouseTouch();
167
168 /// Retrieves the underlying udp touch handler.
169 [[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
170
140 /// Reloads the input devices 171 /// Reloads the input devices
141 void ReloadInputDevices(); 172 void ReloadInputDevices();
142 173
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
deleted file mode 100644
index d4da5596b..000000000
--- a/src/input_common/motion_emu.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <chrono>
7#include <mutex>
8#include <thread>
9#include <tuple>
10#include "common/math_util.h"
11#include "common/quaternion.h"
12#include "common/thread.h"
13#include "common/vector_math.h"
14#include "input_common/motion_emu.h"
15
16namespace InputCommon {
17
18// Implementation class of the motion emulation device
19class MotionEmuDevice {
20public:
21 explicit MotionEmuDevice(int update_millisecond_, float sensitivity_)
22 : update_millisecond(update_millisecond_),
23 update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
24 std::chrono::milliseconds(update_millisecond))),
25 sensitivity(sensitivity_), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
26
27 ~MotionEmuDevice() {
28 if (motion_emu_thread.joinable()) {
29 shutdown_event.Set();
30 motion_emu_thread.join();
31 }
32 }
33
34 void BeginTilt(int x, int y) {
35 mouse_origin = Common::MakeVec(x, y);
36 is_tilting = true;
37 }
38
39 void Tilt(int x, int y) {
40 if (!is_tilting) {
41 return;
42 }
43
44 std::lock_guard guard{tilt_mutex};
45 const auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
46 if (mouse_move.x == 0 && mouse_move.y == 0) {
47 tilt_angle = 0;
48 } else {
49 tilt_direction = mouse_move.Cast<float>();
50 tilt_angle =
51 std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f);
52 }
53 }
54
55 void EndTilt() {
56 std::lock_guard guard{tilt_mutex};
57 tilt_angle = 0;
58 is_tilting = false;
59 }
60
61 Input::MotionStatus GetStatus() {
62 std::lock_guard guard{status_mutex};
63 return status;
64 }
65
66private:
67 const int update_millisecond;
68 const std::chrono::steady_clock::duration update_duration;
69 const float sensitivity;
70
71 Common::Vec2<int> mouse_origin;
72
73 std::mutex tilt_mutex;
74 Common::Vec2<float> tilt_direction;
75 float tilt_angle = 0;
76
77 bool is_tilting = false;
78
79 Common::Event shutdown_event;
80
81 Input::MotionStatus status;
82 std::mutex status_mutex;
83
84 // Note: always keep the thread declaration at the end so that other objects are initialized
85 // before this!
86 std::thread motion_emu_thread;
87
88 void MotionEmuThread() {
89 auto update_time = std::chrono::steady_clock::now();
90 Common::Quaternion<float> q = Common::MakeQuaternion(Common::Vec3<float>(), 0);
91
92 while (!shutdown_event.WaitUntil(update_time)) {
93 update_time += update_duration;
94 const Common::Quaternion<float> old_q = q;
95
96 {
97 std::lock_guard guard{tilt_mutex};
98
99 // Find the quaternion describing current 3DS tilting
100 q = Common::MakeQuaternion(
101 Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle);
102 }
103
104 const auto inv_q = q.Inverse();
105
106 // Set the gravity vector in world space
107 auto gravity = Common::MakeVec(0.0f, -1.0f, 0.0f);
108
109 // Find the angular rate vector in world space
110 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
111 angular_rate *= static_cast<float>(1000 / update_millisecond) / Common::PI * 180.0f;
112
113 // Transform the two vectors from world space to 3DS space
114 gravity = QuaternionRotate(inv_q, gravity);
115 angular_rate = QuaternionRotate(inv_q, angular_rate);
116
117 // TODO: Calculate the correct rotation vector and orientation matrix
118 const auto matrix4x4 = q.ToMatrix();
119 const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f);
120 const std::array orientation{
121 Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
122 Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
123 Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10]),
124 };
125
126 // Update the sensor state
127 {
128 std::lock_guard guard{status_mutex};
129 status = std::make_tuple(gravity, angular_rate, rotation, orientation);
130 }
131 }
132 }
133};
134
135// Interface wrapper held by input receiver as a unique_ptr. It holds the implementation class as
136// a shared_ptr, which is also observed by the factory class as a weak_ptr. In this way the factory
137// can forward all the inputs to the implementation only when it is valid.
138class MotionEmuDeviceWrapper : public Input::MotionDevice {
139public:
140 explicit MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
141 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
142 }
143
144 Input::MotionStatus GetStatus() const override {
145 return device->GetStatus();
146 }
147
148 std::shared_ptr<MotionEmuDevice> device;
149};
150
151std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
152 const int update_period = params.Get("update_period", 100);
153 const float sensitivity = params.Get("sensitivity", 0.01f);
154 auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
155 // Previously created device is disconnected here. Having two motion devices for 3DS is not
156 // expected.
157 current_device = device_wrapper->device;
158 return device_wrapper;
159}
160
161void MotionEmu::BeginTilt(int x, int y) {
162 if (auto ptr = current_device.lock()) {
163 ptr->BeginTilt(x, y);
164 }
165}
166
167void MotionEmu::Tilt(int x, int y) {
168 if (auto ptr = current_device.lock()) {
169 ptr->Tilt(x, y);
170 }
171}
172
173void MotionEmu::EndTilt() {
174 if (auto ptr = current_device.lock()) {
175 ptr->EndTilt();
176 }
177}
178
179} // namespace InputCommon
diff --git a/src/input_common/motion_emu.h b/src/input_common/motion_emu.h
deleted file mode 100644
index 7a7e22467..000000000
--- a/src/input_common/motion_emu.h
+++ /dev/null
@@ -1,46 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/frontend/input.h"
8
9namespace InputCommon {
10
11class MotionEmuDevice;
12
13class MotionEmu : public Input::Factory<Input::MotionDevice> {
14public:
15 /**
16 * Creates a motion device emulated from mouse input
17 * @param params contains parameters for creating the device:
18 * - "update_period": update period in milliseconds
19 * - "sensitivity": the coefficient converting mouse movement to tilting angle
20 */
21 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
22
23 /**
24 * Signals that a motion sensor tilt has begun.
25 * @param x the x-coordinate of the cursor
26 * @param y the y-coordinate of the cursor
27 */
28 void BeginTilt(int x, int y);
29
30 /**
31 * Signals that a motion sensor tilt is occurring.
32 * @param x the x-coordinate of the cursor
33 * @param y the y-coordinate of the cursor
34 */
35 void Tilt(int x, int y);
36
37 /**
38 * Signals that a motion sensor tilt has ended.
39 */
40 void EndTilt();
41
42private:
43 std::weak_ptr<MotionEmuDevice> current_device;
44};
45
46} // namespace InputCommon
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp
new file mode 100644
index 000000000..d0ee64ad7
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.cpp
@@ -0,0 +1,127 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "common/math_util.h"
7#include "common/param_package.h"
8#include "input_common/mouse/mouse_input.h"
9
10namespace MouseInput {
11
12Mouse::Mouse() {
13 update_thread = std::thread(&Mouse::UpdateThread, this);
14}
15
16Mouse::~Mouse() {
17 update_thread_running = false;
18 if (update_thread.joinable()) {
19 update_thread.join();
20 }
21}
22
23void Mouse::UpdateThread() {
24 constexpr int update_time = 10;
25 while (update_thread_running) {
26 for (MouseInfo& info : mouse_info) {
27 Common::Vec3f angular_direction = {-info.tilt_direction.y, 0.0f,
28 -info.tilt_direction.x};
29
30 info.motion.SetGyroscope(angular_direction * info.tilt_speed);
31 info.motion.UpdateRotation(update_time * 1000);
32 info.motion.UpdateOrientation(update_time * 1000);
33 info.tilt_speed = 0;
34 info.data.motion = info.motion.GetMotion();
35 }
36 if (configuring) {
37 UpdateYuzuSettings();
38 }
39 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
40 }
41}
42
43void Mouse::UpdateYuzuSettings() {
44 MouseStatus pad_status{};
45 if (buttons != 0) {
46 pad_status.button = last_button;
47 mouse_queue.Push(pad_status);
48 }
49}
50
51void Mouse::PressButton(int x, int y, int button_) {
52 if (button_ >= static_cast<int>(mouse_info.size())) {
53 return;
54 }
55
56 int button = 1 << button_;
57 const auto button_index = static_cast<std::size_t>(button_);
58 buttons |= static_cast<u16>(button);
59 last_button = static_cast<MouseButton>(button_);
60
61 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
62 mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
63 mouse_info[button_index].data.pressed = true;
64}
65
66void Mouse::MouseMove(int x, int y) {
67 for (MouseInfo& info : mouse_info) {
68 if (info.data.pressed) {
69 auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin;
70 auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position;
71 info.last_mouse_position = Common::MakeVec(x, y);
72 info.data.axis = {mouse_move.x, -mouse_move.y};
73
74 if (mouse_change.x == 0 && mouse_change.y == 0) {
75 info.tilt_speed = 0;
76 } else {
77 info.tilt_direction = mouse_change.Cast<float>();
78 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
79 }
80 }
81 }
82}
83
84void Mouse::ReleaseButton(int button_) {
85 if (button_ >= static_cast<int>(mouse_info.size())) {
86 return;
87 }
88
89 int button = 1 << button_;
90 const auto button_index = static_cast<std::size_t>(button_);
91 buttons &= static_cast<u16>(0xFF - button);
92
93 mouse_info[button_index].tilt_speed = 0;
94 mouse_info[button_index].data.pressed = false;
95 mouse_info[button_index].data.axis = {0, 0};
96}
97
98void Mouse::BeginConfiguration() {
99 buttons = 0;
100 last_button = MouseButton::Undefined;
101 mouse_queue.Clear();
102 configuring = true;
103}
104
105void Mouse::EndConfiguration() {
106 buttons = 0;
107 last_button = MouseButton::Undefined;
108 mouse_queue.Clear();
109 configuring = false;
110}
111
112Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
113 return mouse_queue;
114}
115
116const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
117 return mouse_queue;
118}
119
120MouseData& Mouse::GetMouseState(std::size_t button) {
121 return mouse_info[button].data;
122}
123
124const MouseData& Mouse::GetMouseState(std::size_t button) const {
125 return mouse_info[button].data;
126}
127} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h
new file mode 100644
index 000000000..761663334
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.h
@@ -0,0 +1,99 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <functional>
9#include <mutex>
10#include <thread>
11#include <unordered_map>
12#include "common/common_types.h"
13#include "common/threadsafe_queue.h"
14#include "core/frontend/input.h"
15#include "input_common/main.h"
16#include "input_common/motion_input.h"
17
18namespace MouseInput {
19
20enum class MouseButton {
21 Left,
22 Wheel,
23 Right,
24 Foward,
25 Backward,
26 Undefined,
27};
28
29struct MouseStatus {
30 MouseButton button{MouseButton::Undefined};
31};
32
33struct MouseData {
34 bool pressed{};
35 std::array<int, 2> axis{};
36 Input::MotionStatus motion{};
37 Input::TouchStatus touch{};
38};
39
40class Mouse {
41public:
42 Mouse();
43 ~Mouse();
44
45 /// Used for polling
46 void BeginConfiguration();
47 void EndConfiguration();
48
49 /**
50 * Signals that a button is pressed.
51 * @param x the x-coordinate of the cursor
52 * @param y the y-coordinate of the cursor
53 * @param button the button pressed
54 */
55 void PressButton(int x, int y, int button_);
56
57 /**
58 * Signals that mouse has moved.
59 * @param x the x-coordinate of the cursor
60 * @param y the y-coordinate of the cursor
61 */
62 void MouseMove(int x, int y);
63
64 /**
65 * Signals that a motion sensor tilt has ended.
66 */
67 void ReleaseButton(int button_);
68
69 [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
70 [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
71
72 [[nodiscard]] MouseData& GetMouseState(std::size_t button);
73 [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
74
75private:
76 void UpdateThread();
77 void UpdateYuzuSettings();
78
79 struct MouseInfo {
80 InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
81 Common::Vec2<int> mouse_origin;
82 Common::Vec2<int> last_mouse_position;
83 bool is_tilting = false;
84 float sensitivity{0.120f};
85
86 float tilt_speed = 0;
87 Common::Vec2<float> tilt_direction;
88 MouseData data;
89 };
90
91 u16 buttons{};
92 std::thread update_thread;
93 MouseButton last_button{MouseButton::Undefined};
94 std::array<MouseInfo, 5> mouse_info;
95 Common::SPSCQueue<MouseStatus> mouse_queue;
96 bool configuring{false};
97 bool update_thread_running{true};
98};
99} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
new file mode 100644
index 000000000..6213f3dbd
--- /dev/null
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -0,0 +1,261 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include <utility>
9#include "common/assert.h"
10#include "common/threadsafe_queue.h"
11#include "input_common/mouse/mouse_input.h"
12#include "input_common/mouse/mouse_poller.h"
13
14namespace InputCommon {
15
16class MouseButton final : public Input::ButtonDevice {
17public:
18 explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_)
19 : button(button_), mouse_input(mouse_input_) {}
20
21 bool GetStatus() const override {
22 return mouse_input->GetMouseState(button).pressed;
23 }
24
25private:
26 const u32 button;
27 const MouseInput::Mouse* mouse_input;
28};
29
30MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
31 : mouse_input(std::move(mouse_input_)) {}
32
33std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(
34 const Common::ParamPackage& params) {
35 const auto button_id = params.Get("button", 0);
36
37 return std::make_unique<MouseButton>(button_id, mouse_input.get());
38}
39
40Common::ParamPackage MouseButtonFactory::GetNextInput() const {
41 MouseInput::MouseStatus pad;
42 Common::ParamPackage params;
43 auto& queue = mouse_input->GetMouseQueue();
44 while (queue.Pop(pad)) {
45 // This while loop will break on the earliest detected button
46 if (pad.button != MouseInput::MouseButton::Undefined) {
47 params.Set("engine", "mouse");
48 params.Set("button", static_cast<u16>(pad.button));
49 return params;
50 }
51 }
52 return params;
53}
54
55void MouseButtonFactory::BeginConfiguration() {
56 polling = true;
57 mouse_input->BeginConfiguration();
58}
59
60void MouseButtonFactory::EndConfiguration() {
61 polling = false;
62 mouse_input->EndConfiguration();
63}
64
65class MouseAnalog final : public Input::AnalogDevice {
66public:
67 explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, float range_,
68 const MouseInput::Mouse* mouse_input_)
69 : button(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), range(range_),
70 mouse_input(mouse_input_) {}
71
72 float GetAxis(u32 axis) const {
73 std::lock_guard lock{mutex};
74 const auto axis_value =
75 static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
76 return axis_value / (100.0f * range);
77 }
78
79 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
80 float x = GetAxis(analog_axis_x);
81 float y = GetAxis(analog_axis_y);
82
83 // Make sure the coordinates are in the unit circle,
84 // otherwise normalize it.
85 float r = x * x + y * y;
86 if (r > 1.0f) {
87 r = std::sqrt(r);
88 x /= r;
89 y /= r;
90 }
91
92 return {x, y};
93 }
94
95 std::tuple<float, float> GetStatus() const override {
96 const auto [x, y] = GetAnalog(axis_x, axis_y);
97 const float r = std::sqrt((x * x) + (y * y));
98 if (r > deadzone) {
99 return {x / r * (r - deadzone) / (1 - deadzone),
100 y / r * (r - deadzone) / (1 - deadzone)};
101 }
102 return {0.0f, 0.0f};
103 }
104
105private:
106 const u32 button;
107 const u32 axis_x;
108 const u32 axis_y;
109 const float deadzone;
110 const float range;
111 const MouseInput::Mouse* mouse_input;
112 mutable std::mutex mutex;
113};
114
115/// An analog device factory that creates analog devices from GC Adapter
116MouseAnalogFactory::MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
117 : mouse_input(std::move(mouse_input_)) {}
118
119/**
120 * Creates analog device from joystick axes
121 * @param params contains parameters for creating the device:
122 * - "port": the nth gcpad on the adapter
123 * - "axis_x": the index of the axis to be bind as x-axis
124 * - "axis_y": the index of the axis to be bind as y-axis
125 */
126std::unique_ptr<Input::AnalogDevice> MouseAnalogFactory::Create(
127 const Common::ParamPackage& params) {
128 const auto port = static_cast<u32>(params.Get("port", 0));
129 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
130 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
131 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
132 const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
133
134 return std::make_unique<MouseAnalog>(port, axis_x, axis_y, deadzone, range, mouse_input.get());
135}
136
137void MouseAnalogFactory::BeginConfiguration() {
138 polling = true;
139 mouse_input->BeginConfiguration();
140}
141
142void MouseAnalogFactory::EndConfiguration() {
143 polling = false;
144 mouse_input->EndConfiguration();
145}
146
147Common::ParamPackage MouseAnalogFactory::GetNextInput() const {
148 MouseInput::MouseStatus pad;
149 Common::ParamPackage params;
150 auto& queue = mouse_input->GetMouseQueue();
151 while (queue.Pop(pad)) {
152 // This while loop will break on the earliest detected button
153 if (pad.button != MouseInput::MouseButton::Undefined) {
154 params.Set("engine", "mouse");
155 params.Set("port", static_cast<u16>(pad.button));
156 params.Set("axis_x", 0);
157 params.Set("axis_y", 1);
158 return params;
159 }
160 }
161 return params;
162}
163
164class MouseMotion final : public Input::MotionDevice {
165public:
166 explicit MouseMotion(u32 button_, const MouseInput::Mouse* mouse_input_)
167 : button(button_), mouse_input(mouse_input_) {}
168
169 Input::MotionStatus GetStatus() const override {
170 return mouse_input->GetMouseState(button).motion;
171 }
172
173private:
174 const u32 button;
175 const MouseInput::Mouse* mouse_input;
176};
177
178MouseMotionFactory::MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
179 : mouse_input(std::move(mouse_input_)) {}
180
181std::unique_ptr<Input::MotionDevice> MouseMotionFactory::Create(
182 const Common::ParamPackage& params) {
183 const auto button_id = params.Get("button", 0);
184
185 return std::make_unique<MouseMotion>(button_id, mouse_input.get());
186}
187
188Common::ParamPackage MouseMotionFactory::GetNextInput() const {
189 MouseInput::MouseStatus pad;
190 Common::ParamPackage params;
191 auto& queue = mouse_input->GetMouseQueue();
192 while (queue.Pop(pad)) {
193 // This while loop will break on the earliest detected button
194 if (pad.button != MouseInput::MouseButton::Undefined) {
195 params.Set("engine", "mouse");
196 params.Set("button", static_cast<u16>(pad.button));
197 return params;
198 }
199 }
200 return params;
201}
202
203void MouseMotionFactory::BeginConfiguration() {
204 polling = true;
205 mouse_input->BeginConfiguration();
206}
207
208void MouseMotionFactory::EndConfiguration() {
209 polling = false;
210 mouse_input->EndConfiguration();
211}
212
213class MouseTouch final : public Input::TouchDevice {
214public:
215 explicit MouseTouch(u32 button_, const MouseInput::Mouse* mouse_input_)
216 : button(button_), mouse_input(mouse_input_) {}
217
218 Input::TouchStatus GetStatus() const override {
219 return mouse_input->GetMouseState(button).touch;
220 }
221
222private:
223 const u32 button;
224 const MouseInput::Mouse* mouse_input;
225};
226
227MouseTouchFactory::MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
228 : mouse_input(std::move(mouse_input_)) {}
229
230std::unique_ptr<Input::TouchDevice> MouseTouchFactory::Create(const Common::ParamPackage& params) {
231 const auto button_id = params.Get("button", 0);
232
233 return std::make_unique<MouseTouch>(button_id, mouse_input.get());
234}
235
236Common::ParamPackage MouseTouchFactory::GetNextInput() const {
237 MouseInput::MouseStatus pad;
238 Common::ParamPackage params;
239 auto& queue = mouse_input->GetMouseQueue();
240 while (queue.Pop(pad)) {
241 // This while loop will break on the earliest detected button
242 if (pad.button != MouseInput::MouseButton::Undefined) {
243 params.Set("engine", "mouse");
244 params.Set("button", static_cast<u16>(pad.button));
245 return params;
246 }
247 }
248 return params;
249}
250
251void MouseTouchFactory::BeginConfiguration() {
252 polling = true;
253 mouse_input->BeginConfiguration();
254}
255
256void MouseTouchFactory::EndConfiguration() {
257 polling = false;
258 mouse_input->EndConfiguration();
259}
260
261} // namespace InputCommon
diff --git a/src/input_common/mouse/mouse_poller.h b/src/input_common/mouse/mouse_poller.h
new file mode 100644
index 000000000..cf331293b
--- /dev/null
+++ b/src/input_common/mouse/mouse_poller.h
@@ -0,0 +1,109 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9#include "input_common/mouse/mouse_input.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a mouse. It receives mouse events and forward them
15 * to all button devices it created.
16 */
17class MouseButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28 Common::ParamPackage GetNextInput() const;
29
30 /// For device input configuration/polling
31 void BeginConfiguration();
32 void EndConfiguration();
33
34 bool IsPolling() const {
35 return polling;
36 }
37
38private:
39 std::shared_ptr<MouseInput::Mouse> mouse_input;
40 bool polling = false;
41};
42
43/// An analog device factory that creates analog devices from mouse
44class MouseAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
45public:
46 explicit MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
47
48 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
49
50 Common::ParamPackage GetNextInput() const;
51
52 /// For device input configuration/polling
53 void BeginConfiguration();
54 void EndConfiguration();
55
56 bool IsPolling() const {
57 return polling;
58 }
59
60private:
61 std::shared_ptr<MouseInput::Mouse> mouse_input;
62 bool polling = false;
63};
64
65/// A motion device factory that creates motion devices from mouse
66class MouseMotionFactory final : public Input::Factory<Input::MotionDevice> {
67public:
68 explicit MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
69
70 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
71
72 Common::ParamPackage GetNextInput() const;
73
74 /// For device input configuration/polling
75 void BeginConfiguration();
76 void EndConfiguration();
77
78 bool IsPolling() const {
79 return polling;
80 }
81
82private:
83 std::shared_ptr<MouseInput::Mouse> mouse_input;
84 bool polling = false;
85};
86
87/// An touch device factory that creates touch devices from mouse
88class MouseTouchFactory final : public Input::Factory<Input::TouchDevice> {
89public:
90 explicit MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
91
92 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
93
94 Common::ParamPackage GetNextInput() const;
95
96 /// For device input configuration/polling
97 void BeginConfiguration();
98 void EndConfiguration();
99
100 bool IsPolling() const {
101 return polling;
102 }
103
104private:
105 std::shared_ptr<MouseInput::Mouse> mouse_input;
106 bool polling = false;
107};
108
109} // namespace InputCommon
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index c0bb90048..17a9225d7 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -136,15 +136,7 @@ static void SocketLoop(Socket* socket) {
136 136
137Client::Client() { 137Client::Client() {
138 LOG_INFO(Input, "Udp Initialization started"); 138 LOG_INFO(Input, "Udp Initialization started");
139 for (std::size_t client = 0; client < clients.size(); client++) { 139 ReloadSockets();
140 const auto pad = client % 4;
141 StartCommunication(client, Settings::values.udp_input_address,
142 Settings::values.udp_input_port, pad, 24872);
143 // Set motion parameters
144 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
145 // Real HW values are unknown, 0.0001 is an approximate to Standard
146 clients[client].motion.SetGyroThreshold(0.0001f);
147 }
148} 140}
149 141
150Client::~Client() { 142Client::~Client() {
@@ -167,26 +159,61 @@ std::vector<Common::ParamPackage> Client::GetInputDevices() const {
167 return devices; 159 return devices;
168} 160}
169 161
170bool Client::DeviceConnected(std::size_t pad) const { 162bool Client::DeviceConnected(std::size_t client) const {
171 // Use last timestamp to detect if the socket has stopped sending data 163 // Use last timestamp to detect if the socket has stopped sending data
172 const auto now = std::chrono::system_clock::now(); 164 const auto now = std::chrono::steady_clock::now();
173 const auto time_difference = static_cast<u64>( 165 const auto time_difference =
174 std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update) 166 static_cast<u64>(std::chrono::duration_cast<std::chrono::milliseconds>(
175 .count()); 167 now - clients[client].last_motion_update)
176 return time_difference < 1000 && clients[pad].active == 1; 168 .count());
169 return time_difference < 1000 && clients[client].active == 1;
177} 170}
178 171
179void Client::ReloadUDPClient() { 172void Client::ReloadSockets() {
180 for (std::size_t client = 0; client < clients.size(); client++) { 173 Reset();
181 ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client); 174
175 std::stringstream servers_ss(Settings::values.udp_input_servers);
176 std::string server_token;
177 std::size_t client = 0;
178 while (std::getline(servers_ss, server_token, ',')) {
179 if (client == max_udp_clients) {
180 break;
181 }
182 std::stringstream server_ss(server_token);
183 std::string token;
184 std::getline(server_ss, token, ':');
185 std::string udp_input_address = token;
186 std::getline(server_ss, token, ':');
187 char* temp;
188 const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0));
189 if (*temp != '\0') {
190 LOG_ERROR(Input, "Port number is not valid {}", token);
191 continue;
192 }
193
194 for (std::size_t pad = 0; pad < 4; ++pad) {
195 const std::size_t client_number =
196 GetClientNumber(udp_input_address, udp_input_port, pad);
197 if (client_number != max_udp_clients) {
198 LOG_ERROR(Input, "Duplicated UDP servers found");
199 continue;
200 }
201 StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872);
202 }
182 } 203 }
183} 204}
184void Client::ReloadSocket(const std::string& host, u16 port, std::size_t pad_index, u32 client_id) { 205
185 // client number must be determined from host / port and pad index 206std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t pad) const {
186 const std::size_t client = pad_index; 207 for (std::size_t client = 0; client < clients.size(); client++) {
187 clients[client].socket->Stop(); 208 if (clients[client].active == -1) {
188 clients[client].thread.join(); 209 continue;
189 StartCommunication(client, host, port, pad_index, client_id); 210 }
211 if (clients[client].host == host && clients[client].port == port &&
212 clients[client].pad_index == pad) {
213 return client;
214 }
215 }
216 return max_udp_clients;
190} 217}
191 218
192void Client::OnVersion([[maybe_unused]] Response::Version data) { 219void Client::OnVersion([[maybe_unused]] Response::Version data) {
@@ -197,9 +224,7 @@ void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
197 LOG_TRACE(Input, "PortInfo packet received: {}", data.model); 224 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
198} 225}
199 226
200void Client::OnPadData(Response::PadData data) { 227void Client::OnPadData(Response::PadData data, std::size_t client) {
201 // Client number must be determined from host / port and pad index
202 const std::size_t client = data.info.id;
203 LOG_TRACE(Input, "PadData packet received"); 228 LOG_TRACE(Input, "PadData packet received");
204 if (data.packet_counter == clients[client].packet_sequence) { 229 if (data.packet_counter == clients[client].packet_sequence) {
205 LOG_WARNING( 230 LOG_WARNING(
@@ -208,9 +233,9 @@ void Client::OnPadData(Response::PadData data) {
208 clients[client].packet_sequence, data.packet_counter); 233 clients[client].packet_sequence, data.packet_counter);
209 return; 234 return;
210 } 235 }
211 clients[client].active = data.info.is_pad_active; 236 clients[client].active = static_cast<s8>(data.info.is_pad_active);
212 clients[client].packet_sequence = data.packet_counter; 237 clients[client].packet_sequence = data.packet_counter;
213 const auto now = std::chrono::system_clock::now(); 238 const auto now = std::chrono::steady_clock::now();
214 const auto time_difference = 239 const auto time_difference =
215 static_cast<u64>(std::chrono::duration_cast<std::chrono::microseconds>( 240 static_cast<u64>(std::chrono::duration_cast<std::chrono::microseconds>(
216 now - clients[client].last_motion_update) 241 now - clients[client].last_motion_update)
@@ -264,16 +289,28 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16
264 std::size_t pad_index, u32 client_id) { 289 std::size_t pad_index, u32 client_id) {
265 SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, 290 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
266 [this](Response::PortInfo info) { OnPortInfo(info); }, 291 [this](Response::PortInfo info) { OnPortInfo(info); },
267 [this](Response::PadData data) { OnPadData(data); }}; 292 [this, client](Response::PadData data) { OnPadData(data, client); }};
268 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); 293 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}:{}", host, port,
294 pad_index);
295 clients[client].host = host;
296 clients[client].port = port;
297 clients[client].pad_index = pad_index;
298 clients[client].active = 0;
269 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); 299 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
270 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; 300 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
301 // Set motion parameters
302 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
303 // Real HW values are unknown, 0.0001 is an approximate to Standard
304 clients[client].motion.SetGyroThreshold(0.0001f);
271} 305}
272 306
273void Client::Reset() { 307void Client::Reset() {
274 for (auto& client : clients) { 308 for (auto& client : clients) {
275 client.socket->Stop(); 309 if (client.thread.joinable()) {
276 client.thread.join(); 310 client.active = -1;
311 client.socket->Stop();
312 client.thread.join();
313 }
277 } 314 }
278} 315}
279 316
@@ -283,52 +320,60 @@ void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& a
283 LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}", 320 LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}",
284 client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch); 321 client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch);
285 } 322 }
286 UDPPadStatus pad; 323 UDPPadStatus pad{
324 .host = clients[client].host,
325 .port = clients[client].port,
326 .pad_index = clients[client].pad_index,
327 };
287 if (touch) { 328 if (touch) {
288 pad.touch = PadTouch::Click; 329 pad.touch = PadTouch::Click;
289 pad_queue[client].Push(pad); 330 pad_queue.Push(pad);
290 } 331 }
291 for (size_t i = 0; i < 3; ++i) { 332 for (size_t i = 0; i < 3; ++i) {
292 if (gyro[i] > 5.0f || gyro[i] < -5.0f) { 333 if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
293 pad.motion = static_cast<PadMotion>(i); 334 pad.motion = static_cast<PadMotion>(i);
294 pad.motion_value = gyro[i]; 335 pad.motion_value = gyro[i];
295 pad_queue[client].Push(pad); 336 pad_queue.Push(pad);
296 } 337 }
297 if (acc[i] > 1.75f || acc[i] < -1.75f) { 338 if (acc[i] > 1.75f || acc[i] < -1.75f) {
298 pad.motion = static_cast<PadMotion>(i + 3); 339 pad.motion = static_cast<PadMotion>(i + 3);
299 pad.motion_value = acc[i]; 340 pad.motion_value = acc[i];
300 pad_queue[client].Push(pad); 341 pad_queue.Push(pad);
301 } 342 }
302 } 343 }
303} 344}
304 345
305void Client::BeginConfiguration() { 346void Client::BeginConfiguration() {
306 for (auto& pq : pad_queue) { 347 pad_queue.Clear();
307 pq.Clear();
308 }
309 configuring = true; 348 configuring = true;
310} 349}
311 350
312void Client::EndConfiguration() { 351void Client::EndConfiguration() {
313 for (auto& pq : pad_queue) { 352 pad_queue.Clear();
314 pq.Clear();
315 }
316 configuring = false; 353 configuring = false;
317} 354}
318 355
319DeviceStatus& Client::GetPadState(std::size_t pad) { 356DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) {
320 return clients[pad].status; 357 const std::size_t client_number = GetClientNumber(host, port, pad);
358 if (client_number == max_udp_clients) {
359 return clients[0].status;
360 }
361 return clients[client_number].status;
321} 362}
322 363
323const DeviceStatus& Client::GetPadState(std::size_t pad) const { 364const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const {
324 return clients[pad].status; 365 const std::size_t client_number = GetClientNumber(host, port, pad);
366 if (client_number == max_udp_clients) {
367 return clients[0].status;
368 }
369 return clients[client_number].status;
325} 370}
326 371
327std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() { 372Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
328 return pad_queue; 373 return pad_queue;
329} 374}
330 375
331const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const { 376const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
332 return pad_queue; 377 return pad_queue;
333} 378}
334 379
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index 747e0c0a2..00c8b09f5 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -21,8 +21,7 @@
21 21
22namespace InputCommon::CemuhookUDP { 22namespace InputCommon::CemuhookUDP {
23 23
24constexpr u16 DEFAULT_PORT = 26760; 24constexpr char DEFAULT_SRV[] = "127.0.0.1:26760";
25constexpr char DEFAULT_ADDR[] = "127.0.0.1";
26 25
27class Socket; 26class Socket;
28 27
@@ -48,6 +47,9 @@ enum class PadTouch {
48}; 47};
49 48
50struct UDPPadStatus { 49struct UDPPadStatus {
50 std::string host{"127.0.0.1"};
51 u16 port{26760};
52 std::size_t pad_index{};
51 PadTouch touch{PadTouch::Undefined}; 53 PadTouch touch{PadTouch::Undefined};
52 PadMotion motion{PadMotion::Undefined}; 54 PadMotion motion{PadMotion::Undefined};
53 f32 motion_value{0.0f}; 55 f32 motion_value{0.0f};
@@ -82,37 +84,41 @@ public:
82 84
83 std::vector<Common::ParamPackage> GetInputDevices() const; 85 std::vector<Common::ParamPackage> GetInputDevices() const;
84 86
85 bool DeviceConnected(std::size_t pad) const; 87 bool DeviceConnected(std::size_t client) const;
86 void ReloadUDPClient(); 88 void ReloadSockets();
87 void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760,
88 std::size_t pad_index = 0, u32 client_id = 24872);
89 89
90 std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue(); 90 Common::SPSCQueue<UDPPadStatus>& GetPadQueue();
91 const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const; 91 const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const;
92 92
93 DeviceStatus& GetPadState(std::size_t pad); 93 DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad);
94 const DeviceStatus& GetPadState(std::size_t pad) const; 94 const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const;
95 95
96private: 96private:
97 struct ClientData { 97 struct ClientData {
98 std::string host{"127.0.0.1"};
99 u16 port{26760};
100 std::size_t pad_index{};
98 std::unique_ptr<Socket> socket; 101 std::unique_ptr<Socket> socket;
99 DeviceStatus status; 102 DeviceStatus status;
100 std::thread thread; 103 std::thread thread;
101 u64 packet_sequence = 0; 104 u64 packet_sequence{};
102 u8 active = 0; 105 s8 active{-1};
103 106
104 // Realtime values 107 // Realtime values
105 // motion is initalized with PID values for drift correction on joycons 108 // motion is initalized with PID values for drift correction on joycons
106 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f}; 109 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
107 std::chrono::time_point<std::chrono::system_clock> last_motion_update; 110 std::chrono::time_point<std::chrono::steady_clock> last_motion_update;
108 }; 111 };
109 112
110 // For shutting down, clear all data, join all threads, release usb 113 // For shutting down, clear all data, join all threads, release usb
111 void Reset(); 114 void Reset();
112 115
116 // Translates configuration to client number
117 std::size_t GetClientNumber(std::string_view host, u16 port, std::size_t pad) const;
118
113 void OnVersion(Response::Version); 119 void OnVersion(Response::Version);
114 void OnPortInfo(Response::PortInfo); 120 void OnPortInfo(Response::PortInfo);
115 void OnPadData(Response::PadData); 121 void OnPadData(Response::PadData, std::size_t client);
116 void StartCommunication(std::size_t client, const std::string& host, u16 port, 122 void StartCommunication(std::size_t client, const std::string& host, u16 port,
117 std::size_t pad_index, u32 client_id); 123 std::size_t pad_index, u32 client_id);
118 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, 124 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
@@ -120,8 +126,10 @@ private:
120 126
121 bool configuring = false; 127 bool configuring = false;
122 128
123 std::array<ClientData, 4> clients; 129 // Allocate clients for 8 udp servers
124 std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue; 130 const std::size_t max_udp_clients = 32;
131 std::array<ClientData, 4 * 8> clients;
132 Common::SPSCQueue<UDPPadStatus> pad_queue;
125}; 133};
126 134
127/// An async job allowing configuration of the touchpad calibration. 135/// 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 71a76a7aa..8686a059c 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -13,17 +13,17 @@ namespace InputCommon {
13 13
14class UDPMotion final : public Input::MotionDevice { 14class UDPMotion final : public Input::MotionDevice {
15public: 15public:
16 explicit UDPMotion(std::string ip_, int port_, u32 pad_, CemuhookUDP::Client* client_) 16 explicit UDPMotion(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
17 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} 17 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
18 18
19 Input::MotionStatus GetStatus() const override { 19 Input::MotionStatus GetStatus() const override {
20 return client->GetPadState(pad).motion_status; 20 return client->GetPadState(ip, port, pad).motion_status;
21 } 21 }
22 22
23private: 23private:
24 const std::string ip; 24 const std::string ip;
25 const int port; 25 const u16 port;
26 const u32 pad; 26 const u16 pad;
27 CemuhookUDP::Client* client; 27 CemuhookUDP::Client* client;
28 mutable std::mutex mutex; 28 mutable std::mutex mutex;
29}; 29};
@@ -39,8 +39,8 @@ UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
39 */ 39 */
40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { 40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
41 auto ip = params.Get("ip", "127.0.0.1"); 41 auto ip = params.Get("ip", "127.0.0.1");
42 const auto port = params.Get("port", 26760); 42 const auto port = static_cast<u16>(params.Get("port", 26760));
43 const auto pad = static_cast<u32>(params.Get("pad_index", 0)); 43 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
44 44
45 return std::make_unique<UDPMotion>(std::move(ip), port, pad, client.get()); 45 return std::make_unique<UDPMotion>(std::move(ip), port, pad, client.get());
46} 46}
@@ -59,35 +59,33 @@ Common::ParamPackage UDPMotionFactory::GetNextInput() {
59 Common::ParamPackage params; 59 Common::ParamPackage params;
60 CemuhookUDP::UDPPadStatus pad; 60 CemuhookUDP::UDPPadStatus pad;
61 auto& queue = client->GetPadQueue(); 61 auto& queue = client->GetPadQueue();
62 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { 62 while (queue.Pop(pad)) {
63 while (queue[pad_number].Pop(pad)) { 63 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
64 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) { 64 continue;
65 continue;
66 }
67 params.Set("engine", "cemuhookudp");
68 params.Set("ip", "127.0.0.1");
69 params.Set("port", 26760);
70 params.Set("pad_index", static_cast<int>(pad_number));
71 params.Set("motion", static_cast<u16>(pad.motion));
72 return params;
73 } 65 }
66 params.Set("engine", "cemuhookudp");
67 params.Set("ip", pad.host);
68 params.Set("port", static_cast<u16>(pad.port));
69 params.Set("pad_index", static_cast<u16>(pad.pad_index));
70 params.Set("motion", static_cast<u16>(pad.motion));
71 return params;
74 } 72 }
75 return params; 73 return params;
76} 74}
77 75
78class UDPTouch final : public Input::TouchDevice { 76class UDPTouch final : public Input::TouchDevice {
79public: 77public:
80 explicit UDPTouch(std::string ip_, int port_, u32 pad_, CemuhookUDP::Client* client_) 78 explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
81 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} 79 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
82 80
83 std::tuple<float, float, bool> GetStatus() const override { 81 std::tuple<float, float, bool> GetStatus() const override {
84 return client->GetPadState(pad).touch_status; 82 return client->GetPadState(ip, port, pad).touch_status;
85 } 83 }
86 84
87private: 85private:
88 const std::string ip; 86 const std::string ip;
89 const int port; 87 const u16 port;
90 const u32 pad; 88 const u16 pad;
91 CemuhookUDP::Client* client; 89 CemuhookUDP::Client* client;
92 mutable std::mutex mutex; 90 mutable std::mutex mutex;
93}; 91};
@@ -103,8 +101,8 @@ UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
103 */ 101 */
104std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { 102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
105 auto ip = params.Get("ip", "127.0.0.1"); 103 auto ip = params.Get("ip", "127.0.0.1");
106 const auto port = params.Get("port", 26760); 104 const auto port = static_cast<u16>(params.Get("port", 26760));
107 const auto pad = static_cast<u32>(params.Get("pad_index", 0)); 105 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
108 106
109 return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get()); 107 return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get());
110} 108}
@@ -123,18 +121,16 @@ Common::ParamPackage UDPTouchFactory::GetNextInput() {
123 Common::ParamPackage params; 121 Common::ParamPackage params;
124 CemuhookUDP::UDPPadStatus pad; 122 CemuhookUDP::UDPPadStatus pad;
125 auto& queue = client->GetPadQueue(); 123 auto& queue = client->GetPadQueue();
126 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { 124 while (queue.Pop(pad)) {
127 while (queue[pad_number].Pop(pad)) { 125 if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
128 if (pad.touch == CemuhookUDP::PadTouch::Undefined) { 126 continue;
129 continue;
130 }
131 params.Set("engine", "cemuhookudp");
132 params.Set("ip", "127.0.0.1");
133 params.Set("port", 26760);
134 params.Set("pad_index", static_cast<int>(pad_number));
135 params.Set("touch", static_cast<u16>(pad.touch));
136 return params;
137 } 127 }
128 params.Set("engine", "cemuhookudp");
129 params.Set("ip", pad.host);
130 params.Set("port", static_cast<u16>(pad.port));
131 params.Set("pad_index", static_cast<u16>(pad.pad_index));
132 params.Set("touch", static_cast<u16>(pad.touch));
133 return params;
138 } 134 }
139 return params; 135 return params;
140} 136}