summaryrefslogtreecommitdiff
path: root/src/input_common/mouse
diff options
context:
space:
mode:
authorGravatar Levi2021-01-10 22:09:56 -0700
committerGravatar Levi2021-01-10 22:09:56 -0700
commit7a3c884e39fccfbb498b855080bffabc9ce2e7f1 (patch)
tree5056f9406dec188439cb0deb87603498243a9412 /src/input_common/mouse
parentMore forgetting... duh (diff)
parentMerge pull request #5229 from Morph1984/fullscreen-opt (diff)
downloadyuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.tar.gz
yuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.tar.xz
yuzu-7a3c884e39fccfbb498b855080bffabc9ce2e7f1.zip
Merge remote-tracking branch 'upstream/master' into int-flags
Diffstat (limited to 'src/input_common/mouse')
-rw-r--r--src/input_common/mouse/mouse_input.cpp129
-rw-r--r--src/input_common/mouse/mouse_input.h98
-rw-r--r--src/input_common/mouse/mouse_poller.cpp274
-rw-r--r--src/input_common/mouse/mouse_poller.h109
4 files changed, 610 insertions, 0 deletions
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp
new file mode 100644
index 000000000..10786a541
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.cpp
@@ -0,0 +1,129 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include "input_common/mouse/mouse_input.h"
6
7namespace MouseInput {
8
9Mouse::Mouse() {
10 update_thread = std::thread(&Mouse::UpdateThread, this);
11}
12
13Mouse::~Mouse() {
14 update_thread_running = false;
15 if (update_thread.joinable()) {
16 update_thread.join();
17 }
18}
19
20void Mouse::UpdateThread() {
21 constexpr int update_time = 10;
22 while (update_thread_running) {
23 for (MouseInfo& info : mouse_info) {
24 const Common::Vec3f angular_direction{
25 -info.tilt_direction.y,
26 0.0f,
27 -info.tilt_direction.x,
28 };
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 if (buttons == 0) {
45 return;
46 }
47
48 mouse_queue.Push(MouseStatus{
49 .button = last_button,
50 });
51}
52
53void Mouse::PressButton(int x, int y, int button_) {
54 const auto button_index = static_cast<std::size_t>(button_);
55 if (button_index >= mouse_info.size()) {
56 return;
57 }
58
59 const auto button = 1U << button_index;
60 buttons |= static_cast<u16>(button);
61 last_button = static_cast<MouseButton>(button_index);
62
63 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
64 mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
65 mouse_info[button_index].data.pressed = true;
66}
67
68void Mouse::MouseMove(int x, int y) {
69 for (MouseInfo& info : mouse_info) {
70 if (info.data.pressed) {
71 const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin;
72 const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position;
73 info.last_mouse_position = Common::MakeVec(x, y);
74 info.data.axis = {mouse_move.x, -mouse_move.y};
75
76 if (mouse_change.x == 0 && mouse_change.y == 0) {
77 info.tilt_speed = 0;
78 } else {
79 info.tilt_direction = mouse_change.Cast<float>();
80 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
81 }
82 }
83 }
84}
85
86void Mouse::ReleaseButton(int button_) {
87 const auto button_index = static_cast<std::size_t>(button_);
88 if (button_index >= mouse_info.size()) {
89 return;
90 }
91
92 const auto button = 1U << button_index;
93 buttons &= static_cast<u16>(0xFF - button);
94
95 mouse_info[button_index].tilt_speed = 0;
96 mouse_info[button_index].data.pressed = false;
97 mouse_info[button_index].data.axis = {0, 0};
98}
99
100void Mouse::BeginConfiguration() {
101 buttons = 0;
102 last_button = MouseButton::Undefined;
103 mouse_queue.Clear();
104 configuring = true;
105}
106
107void Mouse::EndConfiguration() {
108 buttons = 0;
109 last_button = MouseButton::Undefined;
110 mouse_queue.Clear();
111 configuring = false;
112}
113
114Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
115 return mouse_queue;
116}
117
118const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
119 return mouse_queue;
120}
121
122MouseData& Mouse::GetMouseState(std::size_t button) {
123 return mouse_info[button].data;
124}
125
126const MouseData& Mouse::GetMouseState(std::size_t button) const {
127 return mouse_info[button].data;
128}
129} // 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..58803c1bf
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.h
@@ -0,0 +1,98 @@
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 <array>
8#include <mutex>
9#include <thread>
10
11#include "common/common_types.h"
12#include "common/threadsafe_queue.h"
13#include "common/vector_math.h"
14#include "core/frontend/input.h"
15#include "input_common/motion_input.h"
16
17namespace MouseInput {
18
19enum class MouseButton {
20 Left,
21 Wheel,
22 Right,
23 Forward,
24 Backward,
25 Undefined,
26};
27
28struct MouseStatus {
29 MouseButton button{MouseButton::Undefined};
30};
31
32struct MouseData {
33 bool pressed{};
34 std::array<int, 2> axis{};
35 Input::MotionStatus motion{};
36 Input::TouchStatus touch{};
37};
38
39class Mouse {
40public:
41 Mouse();
42 ~Mouse();
43
44 /// Used for polling
45 void BeginConfiguration();
46 void EndConfiguration();
47
48 /**
49 * Signals that a button is pressed.
50 * @param x the x-coordinate of the cursor
51 * @param y the y-coordinate of the cursor
52 * @param button_ the button pressed
53 */
54 void PressButton(int x, int y, int button_);
55
56 /**
57 * Signals that mouse has moved.
58 * @param x the x-coordinate of the cursor
59 * @param y the y-coordinate of the cursor
60 */
61 void MouseMove(int x, int y);
62
63 /**
64 * Signals that a motion sensor tilt has ended.
65 */
66 void ReleaseButton(int button_);
67
68 [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
69 [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
70
71 [[nodiscard]] MouseData& GetMouseState(std::size_t button);
72 [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
73
74private:
75 void UpdateThread();
76 void UpdateYuzuSettings();
77
78 struct MouseInfo {
79 InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
80 Common::Vec2<int> mouse_origin;
81 Common::Vec2<int> last_mouse_position;
82 bool is_tilting = false;
83 float sensitivity{0.120f};
84
85 float tilt_speed = 0;
86 Common::Vec2<float> tilt_direction;
87 MouseData data;
88 };
89
90 u16 buttons{};
91 std::thread update_thread;
92 MouseButton last_button{MouseButton::Undefined};
93 std::array<MouseInfo, 5> mouse_info;
94 Common::SPSCQueue<MouseStatus> mouse_queue;
95 bool configuring{false};
96 bool update_thread_running{true};
97};
98} // 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..508eb0c7d
--- /dev/null
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -0,0 +1,274 @@
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 <mutex>
6#include <utility>
7
8#include "common/threadsafe_queue.h"
9#include "input_common/mouse/mouse_input.h"
10#include "input_common/mouse/mouse_poller.h"
11
12namespace InputCommon {
13
14class MouseButton final : public Input::ButtonDevice {
15public:
16 explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_)
17 : button(button_), mouse_input(mouse_input_) {}
18
19 bool GetStatus() const override {
20 return mouse_input->GetMouseState(button).pressed;
21 }
22
23private:
24 const u32 button;
25 const MouseInput::Mouse* mouse_input;
26};
27
28MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
29 : mouse_input(std::move(mouse_input_)) {}
30
31std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(
32 const Common::ParamPackage& params) {
33 const auto button_id = params.Get("button", 0);
34
35 return std::make_unique<MouseButton>(button_id, mouse_input.get());
36}
37
38Common::ParamPackage MouseButtonFactory::GetNextInput() const {
39 MouseInput::MouseStatus pad;
40 Common::ParamPackage params;
41 auto& queue = mouse_input->GetMouseQueue();
42 while (queue.Pop(pad)) {
43 // This while loop will break on the earliest detected button
44 if (pad.button != MouseInput::MouseButton::Undefined) {
45 params.Set("engine", "mouse");
46 params.Set("button", static_cast<u16>(pad.button));
47 return params;
48 }
49 }
50 return params;
51}
52
53void MouseButtonFactory::BeginConfiguration() {
54 polling = true;
55 mouse_input->BeginConfiguration();
56}
57
58void MouseButtonFactory::EndConfiguration() {
59 polling = false;
60 mouse_input->EndConfiguration();
61}
62
63class MouseAnalog final : public Input::AnalogDevice {
64public:
65 explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_,
66 float deadzone_, float range_, const MouseInput::Mouse* mouse_input_)
67 : button(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
68 deadzone(deadzone_), range(range_), mouse_input(mouse_input_) {}
69
70 float GetAxis(u32 axis) const {
71 std::lock_guard lock{mutex};
72 const auto axis_value =
73 static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
74 return axis_value / (100.0f * range);
75 }
76
77 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
78 float x = GetAxis(analog_axis_x);
79 float y = GetAxis(analog_axis_y);
80 if (invert_x) {
81 x = -x;
82 }
83 if (invert_y) {
84 y = -y;
85 }
86
87 // Make sure the coordinates are in the unit circle,
88 // otherwise normalize it.
89 float r = x * x + y * y;
90 if (r > 1.0f) {
91 r = std::sqrt(r);
92 x /= r;
93 y /= r;
94 }
95
96 return {x, y};
97 }
98
99 std::tuple<float, float> GetStatus() const override {
100 const auto [x, y] = GetAnalog(axis_x, axis_y);
101 const float r = std::sqrt((x * x) + (y * y));
102 if (r > deadzone) {
103 return {x / r * (r - deadzone) / (1 - deadzone),
104 y / r * (r - deadzone) / (1 - deadzone)};
105 }
106 return {0.0f, 0.0f};
107 }
108
109private:
110 const u32 button;
111 const u32 axis_x;
112 const u32 axis_y;
113 const bool invert_x;
114 const bool invert_y;
115 const float deadzone;
116 const float range;
117 const MouseInput::Mouse* mouse_input;
118 mutable std::mutex mutex;
119};
120
121/// An analog device factory that creates analog devices from GC Adapter
122MouseAnalogFactory::MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
123 : mouse_input(std::move(mouse_input_)) {}
124
125/**
126 * Creates analog device from joystick axes
127 * @param params contains parameters for creating the device:
128 * - "port": the nth gcpad on the adapter
129 * - "axis_x": the index of the axis to be bind as x-axis
130 * - "axis_y": the index of the axis to be bind as y-axis
131 */
132std::unique_ptr<Input::AnalogDevice> MouseAnalogFactory::Create(
133 const Common::ParamPackage& params) {
134 const auto port = static_cast<u32>(params.Get("port", 0));
135 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
136 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
137 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
138 const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
139 const std::string invert_x_value = params.Get("invert_x", "+");
140 const std::string invert_y_value = params.Get("invert_y", "+");
141 const bool invert_x = invert_x_value == "-";
142 const bool invert_y = invert_y_value == "-";
143
144 return std::make_unique<MouseAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
145 mouse_input.get());
146}
147
148void MouseAnalogFactory::BeginConfiguration() {
149 polling = true;
150 mouse_input->BeginConfiguration();
151}
152
153void MouseAnalogFactory::EndConfiguration() {
154 polling = false;
155 mouse_input->EndConfiguration();
156}
157
158Common::ParamPackage MouseAnalogFactory::GetNextInput() const {
159 MouseInput::MouseStatus pad;
160 Common::ParamPackage params;
161 auto& queue = mouse_input->GetMouseQueue();
162 while (queue.Pop(pad)) {
163 // This while loop will break on the earliest detected button
164 if (pad.button != MouseInput::MouseButton::Undefined) {
165 params.Set("engine", "mouse");
166 params.Set("port", static_cast<u16>(pad.button));
167 params.Set("axis_x", 0);
168 params.Set("axis_y", 1);
169 params.Set("invert_x", "+");
170 params.Set("invert_y", "+");
171 return params;
172 }
173 }
174 return params;
175}
176
177class MouseMotion final : public Input::MotionDevice {
178public:
179 explicit MouseMotion(u32 button_, const MouseInput::Mouse* mouse_input_)
180 : button(button_), mouse_input(mouse_input_) {}
181
182 Input::MotionStatus GetStatus() const override {
183 return mouse_input->GetMouseState(button).motion;
184 }
185
186private:
187 const u32 button;
188 const MouseInput::Mouse* mouse_input;
189};
190
191MouseMotionFactory::MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
192 : mouse_input(std::move(mouse_input_)) {}
193
194std::unique_ptr<Input::MotionDevice> MouseMotionFactory::Create(
195 const Common::ParamPackage& params) {
196 const auto button_id = params.Get("button", 0);
197
198 return std::make_unique<MouseMotion>(button_id, mouse_input.get());
199}
200
201Common::ParamPackage MouseMotionFactory::GetNextInput() const {
202 MouseInput::MouseStatus pad;
203 Common::ParamPackage params;
204 auto& queue = mouse_input->GetMouseQueue();
205 while (queue.Pop(pad)) {
206 // This while loop will break on the earliest detected button
207 if (pad.button != MouseInput::MouseButton::Undefined) {
208 params.Set("engine", "mouse");
209 params.Set("button", static_cast<u16>(pad.button));
210 return params;
211 }
212 }
213 return params;
214}
215
216void MouseMotionFactory::BeginConfiguration() {
217 polling = true;
218 mouse_input->BeginConfiguration();
219}
220
221void MouseMotionFactory::EndConfiguration() {
222 polling = false;
223 mouse_input->EndConfiguration();
224}
225
226class MouseTouch final : public Input::TouchDevice {
227public:
228 explicit MouseTouch(u32 button_, const MouseInput::Mouse* mouse_input_)
229 : button(button_), mouse_input(mouse_input_) {}
230
231 Input::TouchStatus GetStatus() const override {
232 return mouse_input->GetMouseState(button).touch;
233 }
234
235private:
236 const u32 button;
237 const MouseInput::Mouse* mouse_input;
238};
239
240MouseTouchFactory::MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
241 : mouse_input(std::move(mouse_input_)) {}
242
243std::unique_ptr<Input::TouchDevice> MouseTouchFactory::Create(const Common::ParamPackage& params) {
244 const auto button_id = params.Get("button", 0);
245
246 return std::make_unique<MouseTouch>(button_id, mouse_input.get());
247}
248
249Common::ParamPackage MouseTouchFactory::GetNextInput() const {
250 MouseInput::MouseStatus pad;
251 Common::ParamPackage params;
252 auto& queue = mouse_input->GetMouseQueue();
253 while (queue.Pop(pad)) {
254 // This while loop will break on the earliest detected button
255 if (pad.button != MouseInput::MouseButton::Undefined) {
256 params.Set("engine", "mouse");
257 params.Set("button", static_cast<u16>(pad.button));
258 return params;
259 }
260 }
261 return params;
262}
263
264void MouseTouchFactory::BeginConfiguration() {
265 polling = true;
266 mouse_input->BeginConfiguration();
267}
268
269void MouseTouchFactory::EndConfiguration() {
270 polling = false;
271 mouse_input->EndConfiguration();
272}
273
274} // 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