summaryrefslogtreecommitdiff
path: root/src/input_common/mouse
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/mouse')
-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
4 files changed, 596 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..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