summaryrefslogtreecommitdiffstats
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.cpp159
-rw-r--r--src/input_common/mouse/mouse_input.h103
-rw-r--r--src/input_common/mouse/mouse_poller.cpp285
-rw-r--r--src/input_common/mouse/mouse_poller.h109
4 files changed, 656 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..67a584d53
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.cpp
@@ -0,0 +1,159 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "core/settings.h"
+#include "input_common/mouse/mouse_input.h"
+
+namespace MouseInput {
+
+Mouse::Mouse() {
+ update_thread = std::thread(&Mouse::UpdateThread, this);
+}
+
+Mouse::~Mouse() {
+ update_thread_running = false;
+ if (update_thread.joinable()) {
+ update_thread.join();
+ }
+}
+
+void Mouse::UpdateThread() {
+ constexpr int update_time = 10;
+ while (update_thread_running) {
+ for (MouseInfo& info : mouse_info) {
+ const Common::Vec3f angular_direction{
+ -info.tilt_direction.y,
+ 0.0f,
+ -info.tilt_direction.x,
+ };
+
+ info.motion.SetGyroscope(angular_direction * info.tilt_speed);
+ info.motion.UpdateRotation(update_time * 1000);
+ info.motion.UpdateOrientation(update_time * 1000);
+ info.tilt_speed = 0;
+ info.data.motion = info.motion.GetMotion();
+ }
+ if (configuring) {
+ UpdateYuzuSettings();
+ }
+ if (mouse_panning_timout++ > 8) {
+ StopPanning();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
+ }
+}
+
+void Mouse::UpdateYuzuSettings() {
+ if (buttons == 0) {
+ return;
+ }
+
+ mouse_queue.Push(MouseStatus{
+ .button = last_button,
+ });
+}
+
+void Mouse::PressButton(int x, int y, int button_) {
+ const auto button_index = static_cast<std::size_t>(button_);
+ if (button_index >= mouse_info.size()) {
+ return;
+ }
+
+ const auto button = 1U << button_index;
+ buttons |= static_cast<u16>(button);
+ last_button = static_cast<MouseButton>(button_index);
+
+ mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
+ mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
+ mouse_info[button_index].data.pressed = true;
+}
+
+void Mouse::StopPanning() {
+ for (MouseInfo& info : mouse_info) {
+ if (Settings::values.mouse_panning) {
+ info.data.axis = {};
+ info.tilt_speed = 0;
+ info.last_mouse_change = {};
+ }
+ }
+}
+
+void Mouse::MouseMove(int x, int y, int center_x, int center_y) {
+ for (MouseInfo& info : mouse_info) {
+ if (Settings::values.mouse_panning) {
+ const auto mouse_change = Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y);
+ mouse_panning_timout = 0;
+
+ if (mouse_change.y == 0 && mouse_change.x == 0) {
+ continue;
+ }
+
+ info.last_mouse_change = (info.last_mouse_change * 0.8f) + (mouse_change * 0.2f);
+ info.data.axis = {static_cast<int>(16 * info.last_mouse_change.x),
+ static_cast<int>(16 * -info.last_mouse_change.y)};
+ info.tilt_direction = info.last_mouse_change;
+ info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
+ continue;
+ }
+
+ if (info.data.pressed) {
+ const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin;
+ const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position;
+ info.last_mouse_position = Common::MakeVec(x, y);
+ info.data.axis = {mouse_move.x, -mouse_move.y};
+
+ if (mouse_change.x == 0 && mouse_change.y == 0) {
+ info.tilt_speed = 0;
+ } else {
+ info.tilt_direction = mouse_change.Cast<float>();
+ info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
+ }
+ }
+ }
+}
+
+void Mouse::ReleaseButton(int button_) {
+ const auto button_index = static_cast<std::size_t>(button_);
+ if (button_index >= mouse_info.size()) {
+ return;
+ }
+
+ const auto button = 1U << button_index;
+ buttons &= static_cast<u16>(0xFF - button);
+
+ mouse_info[button_index].tilt_speed = 0;
+ mouse_info[button_index].data.pressed = false;
+ mouse_info[button_index].data.axis = {0, 0};
+}
+
+void Mouse::BeginConfiguration() {
+ buttons = 0;
+ last_button = MouseButton::Undefined;
+ mouse_queue.Clear();
+ configuring = true;
+}
+
+void Mouse::EndConfiguration() {
+ buttons = 0;
+ last_button = MouseButton::Undefined;
+ mouse_queue.Clear();
+ configuring = false;
+}
+
+Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
+ return mouse_queue;
+}
+
+const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
+ return mouse_queue;
+}
+
+MouseData& Mouse::GetMouseState(std::size_t button) {
+ return mouse_info[button].data;
+}
+
+const MouseData& Mouse::GetMouseState(std::size_t button) const {
+ return mouse_info[button].data;
+}
+} // 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..46aa676c1
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.h
@@ -0,0 +1,103 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <mutex>
+#include <thread>
+
+#include "common/common_types.h"
+#include "common/threadsafe_queue.h"
+#include "common/vector_math.h"
+#include "core/frontend/input.h"
+#include "input_common/motion_input.h"
+
+namespace MouseInput {
+
+enum class MouseButton {
+ Left,
+ Wheel,
+ Right,
+ Forward,
+ Backward,
+ Undefined,
+};
+
+struct MouseStatus {
+ MouseButton button{MouseButton::Undefined};
+};
+
+struct MouseData {
+ bool pressed{};
+ std::array<int, 2> axis{};
+ Input::MotionStatus motion{};
+ Input::TouchStatus touch{};
+};
+
+class Mouse {
+public:
+ Mouse();
+ ~Mouse();
+
+ /// Used for polling
+ void BeginConfiguration();
+ void EndConfiguration();
+
+ /**
+ * Signals that a button is pressed.
+ * @param x the x-coordinate of the cursor
+ * @param y the y-coordinate of the cursor
+ * @param button_ the button pressed
+ */
+ void PressButton(int x, int y, int button_);
+
+ /**
+ * Signals that mouse has moved.
+ * @param x the x-coordinate of the cursor
+ * @param y the y-coordinate of the cursor
+ * @param center_x the x-coordinate of the middle of the screen
+ * @param center_y the y-coordinate of the middle of the screen
+ */
+ void MouseMove(int x, int y, int center_x, int center_y);
+
+ /**
+ * Signals that a motion sensor tilt has ended.
+ */
+ void ReleaseButton(int button_);
+
+ [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
+ [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
+
+ [[nodiscard]] MouseData& GetMouseState(std::size_t button);
+ [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
+
+private:
+ void UpdateThread();
+ void UpdateYuzuSettings();
+ void StopPanning();
+
+ struct MouseInfo {
+ InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
+ Common::Vec2<int> mouse_origin;
+ Common::Vec2<int> last_mouse_position;
+ Common::Vec2<float> last_mouse_change;
+ bool is_tilting = false;
+ float sensitivity{0.120f};
+
+ float tilt_speed = 0;
+ Common::Vec2<float> tilt_direction;
+ MouseData data;
+ };
+
+ u16 buttons{};
+ std::thread update_thread;
+ MouseButton last_button{MouseButton::Undefined};
+ std::array<MouseInfo, 5> mouse_info;
+ Common::SPSCQueue<MouseStatus> mouse_queue;
+ bool configuring{false};
+ bool update_thread_running{true};
+ int mouse_panning_timout{};
+};
+} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
new file mode 100644
index 000000000..bb56787ee
--- /dev/null
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -0,0 +1,285 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <mutex>
+#include <utility>
+
+#include "common/threadsafe_queue.h"
+#include "core/settings.h"
+#include "input_common/mouse/mouse_input.h"
+#include "input_common/mouse/mouse_poller.h"
+
+namespace InputCommon {
+
+class MouseButton final : public Input::ButtonDevice {
+public:
+ explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_)
+ : button(button_), mouse_input(mouse_input_) {}
+
+ bool GetStatus() const override {
+ return mouse_input->GetMouseState(button).pressed;
+ }
+
+private:
+ const u32 button;
+ const MouseInput::Mouse* mouse_input;
+};
+
+MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
+ : mouse_input(std::move(mouse_input_)) {}
+
+std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(
+ const Common::ParamPackage& params) {
+ const auto button_id = params.Get("button", 0);
+
+ return std::make_unique<MouseButton>(button_id, mouse_input.get());
+}
+
+Common::ParamPackage MouseButtonFactory::GetNextInput() const {
+ MouseInput::MouseStatus pad;
+ Common::ParamPackage params;
+ auto& queue = mouse_input->GetMouseQueue();
+ while (queue.Pop(pad)) {
+ // This while loop will break on the earliest detected button
+ if (pad.button != MouseInput::MouseButton::Undefined) {
+ params.Set("engine", "mouse");
+ params.Set("button", static_cast<u16>(pad.button));
+ return params;
+ }
+ }
+ return params;
+}
+
+void MouseButtonFactory::BeginConfiguration() {
+ polling = true;
+ mouse_input->BeginConfiguration();
+}
+
+void MouseButtonFactory::EndConfiguration() {
+ polling = false;
+ mouse_input->EndConfiguration();
+}
+
+class MouseAnalog final : public Input::AnalogDevice {
+public:
+ explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_,
+ float deadzone_, float range_, const MouseInput::Mouse* mouse_input_)
+ : button(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
+ deadzone(deadzone_), range(range_), mouse_input(mouse_input_) {}
+
+ float GetAxis(u32 axis) const {
+ std::lock_guard lock{mutex};
+ const auto axis_value =
+ static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
+ return axis_value * Settings::values.mouse_panning_sensitivity / (100.0f * range);
+ }
+
+ std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
+ float x = GetAxis(analog_axis_x);
+ float y = GetAxis(analog_axis_y);
+ if (invert_x) {
+ x = -x;
+ }
+ if (invert_y) {
+ y = -y;
+ }
+
+ // Make sure the coordinates are in the unit circle,
+ // otherwise normalize it.
+ float r = x * x + y * y;
+ if (r > 1.0f) {
+ r = std::sqrt(r);
+ x /= r;
+ y /= r;
+ }
+
+ return {x, y};
+ }
+
+ std::tuple<float, float> GetStatus() const override {
+ const auto [x, y] = GetAnalog(axis_x, axis_y);
+ const float r = std::sqrt((x * x) + (y * y));
+ if (r > deadzone) {
+ return {x / r * (r - deadzone) / (1 - deadzone),
+ y / r * (r - deadzone) / (1 - deadzone)};
+ }
+ return {0.0f, 0.0f};
+ }
+
+ std::tuple<float, float> GetRawStatus() const override {
+ const float x = GetAxis(axis_x);
+ const float y = GetAxis(axis_y);
+ return {x, y};
+ }
+
+ Input::AnalogProperties GetAnalogProperties() const override {
+ return {deadzone, range, 0.5f};
+ }
+
+private:
+ const u32 button;
+ const u32 axis_x;
+ const u32 axis_y;
+ const bool invert_x;
+ const bool invert_y;
+ const float deadzone;
+ const float range;
+ const MouseInput::Mouse* mouse_input;
+ mutable std::mutex mutex;
+};
+
+/// An analog device factory that creates analog devices from GC Adapter
+MouseAnalogFactory::MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
+ : mouse_input(std::move(mouse_input_)) {}
+
+/**
+ * Creates analog device from joystick axes
+ * @param params contains parameters for creating the device:
+ * - "port": the nth gcpad on the adapter
+ * - "axis_x": the index of the axis to be bind as x-axis
+ * - "axis_y": the index of the axis to be bind as y-axis
+ */
+std::unique_ptr<Input::AnalogDevice> MouseAnalogFactory::Create(
+ const Common::ParamPackage& params) {
+ const auto port = static_cast<u32>(params.Get("port", 0));
+ const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
+ const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
+ const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
+ const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
+ const std::string invert_x_value = params.Get("invert_x", "+");
+ const std::string invert_y_value = params.Get("invert_y", "+");
+ const bool invert_x = invert_x_value == "-";
+ const bool invert_y = invert_y_value == "-";
+
+ return std::make_unique<MouseAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
+ mouse_input.get());
+}
+
+void MouseAnalogFactory::BeginConfiguration() {
+ polling = true;
+ mouse_input->BeginConfiguration();
+}
+
+void MouseAnalogFactory::EndConfiguration() {
+ polling = false;
+ mouse_input->EndConfiguration();
+}
+
+Common::ParamPackage MouseAnalogFactory::GetNextInput() const {
+ MouseInput::MouseStatus pad;
+ Common::ParamPackage params;
+ auto& queue = mouse_input->GetMouseQueue();
+ while (queue.Pop(pad)) {
+ // This while loop will break on the earliest detected button
+ if (pad.button != MouseInput::MouseButton::Undefined) {
+ params.Set("engine", "mouse");
+ params.Set("port", static_cast<u16>(pad.button));
+ params.Set("axis_x", 0);
+ params.Set("axis_y", 1);
+ params.Set("invert_x", "+");
+ params.Set("invert_y", "+");
+ return params;
+ }
+ }
+ return params;
+}
+
+class MouseMotion final : public Input::MotionDevice {
+public:
+ explicit MouseMotion(u32 button_, const MouseInput::Mouse* mouse_input_)
+ : button(button_), mouse_input(mouse_input_) {}
+
+ Input::MotionStatus GetStatus() const override {
+ return mouse_input->GetMouseState(button).motion;
+ }
+
+private:
+ const u32 button;
+ const MouseInput::Mouse* mouse_input;
+};
+
+MouseMotionFactory::MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
+ : mouse_input(std::move(mouse_input_)) {}
+
+std::unique_ptr<Input::MotionDevice> MouseMotionFactory::Create(
+ const Common::ParamPackage& params) {
+ const auto button_id = params.Get("button", 0);
+
+ return std::make_unique<MouseMotion>(button_id, mouse_input.get());
+}
+
+Common::ParamPackage MouseMotionFactory::GetNextInput() const {
+ MouseInput::MouseStatus pad;
+ Common::ParamPackage params;
+ auto& queue = mouse_input->GetMouseQueue();
+ while (queue.Pop(pad)) {
+ // This while loop will break on the earliest detected button
+ if (pad.button != MouseInput::MouseButton::Undefined) {
+ params.Set("engine", "mouse");
+ params.Set("button", static_cast<u16>(pad.button));
+ return params;
+ }
+ }
+ return params;
+}
+
+void MouseMotionFactory::BeginConfiguration() {
+ polling = true;
+ mouse_input->BeginConfiguration();
+}
+
+void MouseMotionFactory::EndConfiguration() {
+ polling = false;
+ mouse_input->EndConfiguration();
+}
+
+class MouseTouch final : public Input::TouchDevice {
+public:
+ explicit MouseTouch(u32 button_, const MouseInput::Mouse* mouse_input_)
+ : button(button_), mouse_input(mouse_input_) {}
+
+ Input::TouchStatus GetStatus() const override {
+ return mouse_input->GetMouseState(button).touch;
+ }
+
+private:
+ const u32 button;
+ const MouseInput::Mouse* mouse_input;
+};
+
+MouseTouchFactory::MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
+ : mouse_input(std::move(mouse_input_)) {}
+
+std::unique_ptr<Input::TouchDevice> MouseTouchFactory::Create(const Common::ParamPackage& params) {
+ const auto button_id = params.Get("button", 0);
+
+ return std::make_unique<MouseTouch>(button_id, mouse_input.get());
+}
+
+Common::ParamPackage MouseTouchFactory::GetNextInput() const {
+ MouseInput::MouseStatus pad;
+ Common::ParamPackage params;
+ auto& queue = mouse_input->GetMouseQueue();
+ while (queue.Pop(pad)) {
+ // This while loop will break on the earliest detected button
+ if (pad.button != MouseInput::MouseButton::Undefined) {
+ params.Set("engine", "mouse");
+ params.Set("button", static_cast<u16>(pad.button));
+ return params;
+ }
+ }
+ return params;
+}
+
+void MouseTouchFactory::BeginConfiguration() {
+ polling = true;
+ mouse_input->BeginConfiguration();
+}
+
+void MouseTouchFactory::EndConfiguration() {
+ polling = false;
+ mouse_input->EndConfiguration();
+}
+
+} // 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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/frontend/input.h"
+#include "input_common/mouse/mouse_input.h"
+
+namespace InputCommon {
+
+/**
+ * A button device factory representing a mouse. It receives mouse events and forward them
+ * to all button devices it created.
+ */
+class MouseButtonFactory final : public Input::Factory<Input::ButtonDevice> {
+public:
+ explicit MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
+
+ /**
+ * Creates a button device from a button press
+ * @param params contains parameters for creating the device:
+ * - "code": the code of the key to bind with the button
+ */
+ std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
+
+ Common::ParamPackage GetNextInput() const;
+
+ /// For device input configuration/polling
+ void BeginConfiguration();
+ void EndConfiguration();
+
+ bool IsPolling() const {
+ return polling;
+ }
+
+private:
+ std::shared_ptr<MouseInput::Mouse> mouse_input;
+ bool polling = false;
+};
+
+/// An analog device factory that creates analog devices from mouse
+class MouseAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
+public:
+ explicit MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
+
+ std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
+
+ Common::ParamPackage GetNextInput() const;
+
+ /// For device input configuration/polling
+ void BeginConfiguration();
+ void EndConfiguration();
+
+ bool IsPolling() const {
+ return polling;
+ }
+
+private:
+ std::shared_ptr<MouseInput::Mouse> mouse_input;
+ bool polling = false;
+};
+
+/// A motion device factory that creates motion devices from mouse
+class MouseMotionFactory final : public Input::Factory<Input::MotionDevice> {
+public:
+ explicit MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
+
+ std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
+
+ Common::ParamPackage GetNextInput() const;
+
+ /// For device input configuration/polling
+ void BeginConfiguration();
+ void EndConfiguration();
+
+ bool IsPolling() const {
+ return polling;
+ }
+
+private:
+ std::shared_ptr<MouseInput::Mouse> mouse_input;
+ bool polling = false;
+};
+
+/// An touch device factory that creates touch devices from mouse
+class MouseTouchFactory final : public Input::Factory<Input::TouchDevice> {
+public:
+ explicit MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
+
+ std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
+
+ Common::ParamPackage GetNextInput() const;
+
+ /// For device input configuration/polling
+ void BeginConfiguration();
+ void EndConfiguration();
+
+ bool IsPolling() const {
+ return polling;
+ }
+
+private:
+ std::shared_ptr<MouseInput::Mouse> mouse_input;
+ bool polling = false;
+};
+
+} // namespace InputCommon