summaryrefslogtreecommitdiffstats
path: root/src/input_common
diff options
context:
space:
mode:
authorbunnei <bunneidev@gmail.com>2020-11-30 07:59:50 +0100
committerGitHub <noreply@github.com>2020-11-30 07:59:50 +0100
commit7bc3e80399d62aabe4acbba094d0fff23a187186 (patch)
treeac12ca41f34fdda51992fedfeb55b169dedcfcaf /src/input_common
parentMerge pull request #5005 from ReinUsesLisp/div-ceil (diff)
parentImplement full mouse support (diff)
downloadyuzu-7bc3e80399d62aabe4acbba094d0fff23a187186.tar
yuzu-7bc3e80399d62aabe4acbba094d0fff23a187186.tar.gz
yuzu-7bc3e80399d62aabe4acbba094d0fff23a187186.tar.bz2
yuzu-7bc3e80399d62aabe4acbba094d0fff23a187186.tar.lz
yuzu-7bc3e80399d62aabe4acbba094d0fff23a187186.tar.xz
yuzu-7bc3e80399d62aabe4acbba094d0fff23a187186.tar.zst
yuzu-7bc3e80399d62aabe4acbba094d0fff23a187186.zip
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/CMakeLists.txt6
-rw-r--r--src/input_common/main.cpp73
-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.cpp125
-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
9 files changed, 697 insertions, 242 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 5682e5ca5..38ab31898 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -5,8 +5,6 @@ add_library(input_common STATIC
keyboard.h
main.cpp
main.h
- motion_emu.cpp
- motion_emu.h
motion_from_button.cpp
motion_from_button.h
motion_input.cpp
@@ -19,6 +17,10 @@ add_library(input_common STATIC
gcadapter/gc_adapter.h
gcadapter/gc_poller.cpp
gcadapter/gc_poller.h
+ mouse/mouse_input.cpp
+ mouse/mouse_input.h
+ mouse/mouse_poller.cpp
+ mouse/mouse_poller.h
sdl/sdl.cpp
sdl/sdl.h
udp/client.cpp
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index e59ad4ff5..880ea73b8 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -10,8 +10,9 @@
#include "input_common/gcadapter/gc_poller.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
-#include "input_common/motion_emu.h"
#include "input_common/motion_from_button.h"
+#include "input_common/mouse/mouse_input.h"
+#include "input_common/mouse/mouse_poller.h"
#include "input_common/touch_from_button.h"
#include "input_common/udp/client.h"
#include "input_common/udp/udp.h"
@@ -37,8 +38,6 @@ struct InputSubsystem::Impl {
std::make_shared<AnalogFromButton>());
Input::RegisterFactory<Input::MotionDevice>("keyboard",
std::make_shared<MotionFromButton>());
- motion_emu = std::make_shared<MotionEmu>();
- Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
std::make_shared<TouchFromButtonFactory>());
@@ -51,6 +50,16 @@ struct InputSubsystem::Impl {
Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
udptouch = std::make_shared<UDPTouchFactory>(udp);
Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
+
+ mouse = std::make_shared<MouseInput::Mouse>();
+ mousebuttons = std::make_shared<MouseButtonFactory>(mouse);
+ Input::RegisterFactory<Input::ButtonDevice>("mouse", mousebuttons);
+ mouseanalog = std::make_shared<MouseAnalogFactory>(mouse);
+ Input::RegisterFactory<Input::AnalogDevice>("mouse", mouseanalog);
+ mousemotion = std::make_shared<MouseMotionFactory>(mouse);
+ Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
+ mousetouch = std::make_shared<MouseTouchFactory>(mouse);
+ Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
}
void Shutdown() {
@@ -58,8 +67,6 @@ struct InputSubsystem::Impl {
Input::UnregisterFactory<Input::MotionDevice>("keyboard");
keyboard.reset();
Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
- Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
- motion_emu.reset();
Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
#ifdef HAVE_SDL2
sdl.reset();
@@ -77,6 +84,16 @@ struct InputSubsystem::Impl {
udpmotion.reset();
udptouch.reset();
+
+ Input::UnregisterFactory<Input::ButtonDevice>("mouse");
+ Input::UnregisterFactory<Input::AnalogDevice>("mouse");
+ Input::UnregisterFactory<Input::MotionDevice>("mouse");
+ Input::UnregisterFactory<Input::TouchDevice>("mouse");
+
+ mousebuttons.reset();
+ mouseanalog.reset();
+ mousemotion.reset();
+ mousetouch.reset();
}
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
@@ -140,7 +157,6 @@ struct InputSubsystem::Impl {
}
std::shared_ptr<Keyboard> keyboard;
- std::shared_ptr<MotionEmu> motion_emu;
#ifdef HAVE_SDL2
std::unique_ptr<SDL::State> sdl;
#endif
@@ -149,8 +165,13 @@ struct InputSubsystem::Impl {
std::shared_ptr<GCVibrationFactory> gcvibration;
std::shared_ptr<UDPMotionFactory> udpmotion;
std::shared_ptr<UDPTouchFactory> udptouch;
+ std::shared_ptr<MouseButtonFactory> mousebuttons;
+ std::shared_ptr<MouseAnalogFactory> mouseanalog;
+ std::shared_ptr<MouseMotionFactory> mousemotion;
+ std::shared_ptr<MouseTouchFactory> mousetouch;
std::shared_ptr<CemuhookUDP::Client> udp;
std::shared_ptr<GCAdapter::Adapter> gcadapter;
+ std::shared_ptr<MouseInput::Mouse> mouse;
};
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -173,12 +194,12 @@ const Keyboard* InputSubsystem::GetKeyboard() const {
return impl->keyboard.get();
}
-MotionEmu* InputSubsystem::GetMotionEmu() {
- return impl->motion_emu.get();
+MouseInput::Mouse* InputSubsystem::GetMouse() {
+ return impl->mouse.get();
}
-const MotionEmu* InputSubsystem::GetMotionEmu() const {
- return impl->motion_emu.get();
+const MouseInput::Mouse* InputSubsystem::GetMouse() const {
+ return impl->mouse.get();
}
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
@@ -229,6 +250,38 @@ const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
return impl->udptouch.get();
}
+MouseButtonFactory* InputSubsystem::GetMouseButtons() {
+ return impl->mousebuttons.get();
+}
+
+const MouseButtonFactory* InputSubsystem::GetMouseButtons() const {
+ return impl->mousebuttons.get();
+}
+
+MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() {
+ return impl->mouseanalog.get();
+}
+
+const MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() const {
+ return impl->mouseanalog.get();
+}
+
+MouseMotionFactory* InputSubsystem::GetMouseMotions() {
+ return impl->mousemotion.get();
+}
+
+const MouseMotionFactory* InputSubsystem::GetMouseMotions() const {
+ return impl->mousemotion.get();
+}
+
+MouseTouchFactory* InputSubsystem::GetMouseTouch() {
+ return impl->mousetouch.get();
+}
+
+const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
+ return impl->mousetouch.get();
+}
+
void InputSubsystem::ReloadInputDevices() {
if (!impl->udp) {
return;
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 {
enum Values : int;
}
+namespace MouseInput {
+class Mouse;
+}
+
namespace InputCommon {
namespace Polling {
@@ -56,8 +60,11 @@ class GCAnalogFactory;
class GCButtonFactory;
class UDPMotionFactory;
class UDPTouchFactory;
+class MouseButtonFactory;
+class MouseAnalogFactory;
+class MouseMotionFactory;
+class MouseTouchFactory;
class Keyboard;
-class MotionEmu;
/**
* Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
@@ -90,11 +97,11 @@ public:
/// Retrieves the underlying keyboard device.
[[nodiscard]] const Keyboard* GetKeyboard() const;
- /// Retrieves the underlying motion emulation factory.
- [[nodiscard]] MotionEmu* GetMotionEmu();
+ /// Retrieves the underlying mouse device.
+ [[nodiscard]] MouseInput::Mouse* GetMouse();
- /// Retrieves the underlying motion emulation factory.
- [[nodiscard]] const MotionEmu* GetMotionEmu() const;
+ /// Retrieves the underlying mouse device.
+ [[nodiscard]] const MouseInput::Mouse* GetMouse() const;
/**
* Returns all available input devices that this Factory can create a new device with.
@@ -137,6 +144,30 @@ public:
/// Retrieves the underlying udp touch handler.
[[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
+ /// Retrieves the underlying GameCube button handler.
+ [[nodiscard]] MouseButtonFactory* GetMouseButtons();
+
+ /// Retrieves the underlying GameCube button handler.
+ [[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
+
+ /// Retrieves the underlying udp touch handler.
+ [[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
+
+ /// Retrieves the underlying udp touch handler.
+ [[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
+
+ /// Retrieves the underlying udp motion handler.
+ [[nodiscard]] MouseMotionFactory* GetMouseMotions();
+
+ /// Retrieves the underlying udp motion handler.
+ [[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
+
+ /// Retrieves the underlying udp touch handler.
+ [[nodiscard]] MouseTouchFactory* GetMouseTouch();
+
+ /// Retrieves the underlying udp touch handler.
+ [[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
+
/// Reloads the input devices
void ReloadInputDevices();
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 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <chrono>
-#include <mutex>
-#include <thread>
-#include <tuple>
-#include "common/math_util.h"
-#include "common/quaternion.h"
-#include "common/thread.h"
-#include "common/vector_math.h"
-#include "input_common/motion_emu.h"
-
-namespace InputCommon {
-
-// Implementation class of the motion emulation device
-class MotionEmuDevice {
-public:
- explicit MotionEmuDevice(int update_millisecond_, float sensitivity_)
- : update_millisecond(update_millisecond_),
- update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
- std::chrono::milliseconds(update_millisecond))),
- sensitivity(sensitivity_), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
-
- ~MotionEmuDevice() {
- if (motion_emu_thread.joinable()) {
- shutdown_event.Set();
- motion_emu_thread.join();
- }
- }
-
- void BeginTilt(int x, int y) {
- mouse_origin = Common::MakeVec(x, y);
- is_tilting = true;
- }
-
- void Tilt(int x, int y) {
- if (!is_tilting) {
- return;
- }
-
- std::lock_guard guard{tilt_mutex};
- const auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
- if (mouse_move.x == 0 && mouse_move.y == 0) {
- tilt_angle = 0;
- } else {
- tilt_direction = mouse_move.Cast<float>();
- tilt_angle =
- std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f);
- }
- }
-
- void EndTilt() {
- std::lock_guard guard{tilt_mutex};
- tilt_angle = 0;
- is_tilting = false;
- }
-
- Input::MotionStatus GetStatus() {
- std::lock_guard guard{status_mutex};
- return status;
- }
-
-private:
- const int update_millisecond;
- const std::chrono::steady_clock::duration update_duration;
- const float sensitivity;
-
- Common::Vec2<int> mouse_origin;
-
- std::mutex tilt_mutex;
- Common::Vec2<float> tilt_direction;
- float tilt_angle = 0;
-
- bool is_tilting = false;
-
- Common::Event shutdown_event;
-
- Input::MotionStatus status;
- std::mutex status_mutex;
-
- // Note: always keep the thread declaration at the end so that other objects are initialized
- // before this!
- std::thread motion_emu_thread;
-
- void MotionEmuThread() {
- auto update_time = std::chrono::steady_clock::now();
- Common::Quaternion<float> q = Common::MakeQuaternion(Common::Vec3<float>(), 0);
-
- while (!shutdown_event.WaitUntil(update_time)) {
- update_time += update_duration;
- const Common::Quaternion<float> old_q = q;
-
- {
- std::lock_guard guard{tilt_mutex};
-
- // Find the quaternion describing current 3DS tilting
- q = Common::MakeQuaternion(
- Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle);
- }
-
- const auto inv_q = q.Inverse();
-
- // Set the gravity vector in world space
- auto gravity = Common::MakeVec(0.0f, -1.0f, 0.0f);
-
- // Find the angular rate vector in world space
- auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
- angular_rate *= static_cast<float>(1000 / update_millisecond) / Common::PI * 180.0f;
-
- // Transform the two vectors from world space to 3DS space
- gravity = QuaternionRotate(inv_q, gravity);
- angular_rate = QuaternionRotate(inv_q, angular_rate);
-
- // TODO: Calculate the correct rotation vector and orientation matrix
- const auto matrix4x4 = q.ToMatrix();
- const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f);
- const std::array orientation{
- Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
- Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
- Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10]),
- };
-
- // Update the sensor state
- {
- std::lock_guard guard{status_mutex};
- status = std::make_tuple(gravity, angular_rate, rotation, orientation);
- }
- }
- }
-};
-
-// Interface wrapper held by input receiver as a unique_ptr. It holds the implementation class as
-// a shared_ptr, which is also observed by the factory class as a weak_ptr. In this way the factory
-// can forward all the inputs to the implementation only when it is valid.
-class MotionEmuDeviceWrapper : public Input::MotionDevice {
-public:
- explicit MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
- device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
- }
-
- Input::MotionStatus GetStatus() const override {
- return device->GetStatus();
- }
-
- std::shared_ptr<MotionEmuDevice> device;
-};
-
-std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
- const int update_period = params.Get("update_period", 100);
- const float sensitivity = params.Get("sensitivity", 0.01f);
- auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
- // Previously created device is disconnected here. Having two motion devices for 3DS is not
- // expected.
- current_device = device_wrapper->device;
- return device_wrapper;
-}
-
-void MotionEmu::BeginTilt(int x, int y) {
- if (auto ptr = current_device.lock()) {
- ptr->BeginTilt(x, y);
- }
-}
-
-void MotionEmu::Tilt(int x, int y) {
- if (auto ptr = current_device.lock()) {
- ptr->Tilt(x, y);
- }
-}
-
-void MotionEmu::EndTilt() {
- if (auto ptr = current_device.lock()) {
- ptr->EndTilt();
- }
-}
-
-} // 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 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "core/frontend/input.h"
-
-namespace InputCommon {
-
-class MotionEmuDevice;
-
-class MotionEmu : public Input::Factory<Input::MotionDevice> {
-public:
- /**
- * Creates a motion device emulated from mouse input
- * @param params contains parameters for creating the device:
- * - "update_period": update period in milliseconds
- * - "sensitivity": the coefficient converting mouse movement to tilting angle
- */
- std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
-
- /**
- * Signals that a motion sensor tilt has begun.
- * @param x the x-coordinate of the cursor
- * @param y the y-coordinate of the cursor
- */
- void BeginTilt(int x, int y);
-
- /**
- * Signals that a motion sensor tilt is occurring.
- * @param x the x-coordinate of the cursor
- * @param y the y-coordinate of the cursor
- */
- void Tilt(int x, int y);
-
- /**
- * Signals that a motion sensor tilt has ended.
- */
- void EndTilt();
-
-private:
- std::weak_ptr<MotionEmuDevice> current_device;
-};
-
-} // 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..3f4264caa
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.cpp
@@ -0,0 +1,125 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "common/math_util.h"
+#include "common/param_package.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) {
+ 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();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
+ }
+}
+
+void Mouse::UpdateYuzuSettings() {
+ MouseStatus pad_status{};
+ if (buttons != 0) {
+ pad_status.button = last_button;
+ mouse_queue.Push(pad_status);
+ }
+}
+
+void Mouse::PressButton(int x, int y, int button_) {
+ if (button_ >= static_cast<int>(mouse_info.size())) {
+ return;
+ }
+
+ int button = 1 << button_;
+ buttons |= static_cast<u16>(button);
+ last_button = static_cast<MouseButton>(button_);
+
+ mouse_info[button_].mouse_origin = Common::MakeVec(x, y);
+ mouse_info[button_].last_mouse_position = Common::MakeVec(x, y);
+ mouse_info[button_].data.pressed = true;
+}
+
+void Mouse::MouseMove(int x, int y) {
+ for (MouseInfo& info : mouse_info) {
+ if (info.data.pressed) {
+ auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin;
+ 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_) {
+ if (button_ >= static_cast<int>(mouse_info.size())) {
+ return;
+ }
+
+ int button = 1 << button_;
+ buttons &= static_cast<u16>(0xFF - button);
+
+ mouse_info[button_].tilt_speed = 0;
+ mouse_info[button_].data.pressed = false;
+ mouse_info[button_].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..761663334
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.h
@@ -0,0 +1,99 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include "common/common_types.h"
+#include "common/threadsafe_queue.h"
+#include "core/frontend/input.h"
+#include "input_common/main.h"
+#include "input_common/motion_input.h"
+
+namespace MouseInput {
+
+enum class MouseButton {
+ Left,
+ Wheel,
+ Right,
+ Foward,
+ 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
+ */
+ void MouseMove(int x, int 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();
+
+ struct MouseInfo {
+ InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
+ Common::Vec2<int> mouse_origin;
+ Common::Vec2<int> last_mouse_position;
+ 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};
+};
+} // 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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <atomic>
+#include <list>
+#include <mutex>
+#include <utility>
+#include "common/assert.h"
+#include "common/threadsafe_queue.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_, float deadzone_, float range_,
+ const MouseInput::Mouse* mouse_input_)
+ : button(port_), axis_x(axis_x_), axis_y(axis_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 / (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);
+
+ // 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};
+ }
+
+private:
+ const u32 button;
+ const u32 axis_x;
+ const u32 axis_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);
+
+ return std::make_unique<MouseAnalog>(port, axis_x, axis_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);
+ 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