diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 25ac5af46..93251e3aa 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -161,10 +161,15 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common
 using MotionDevice = InputDevice<MotionStatus>;
 
 /**
- * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
+ * A touch status is an object that returns a tuple of two floats and a bool. The floats are
  * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
  */
-using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
+using TouchStatus = std::tuple<float, float, bool>;
+
+/**
+ * A touch device is an input device that returns a touch status object
+ */
+using TouchDevice = InputDevice<TouchStatus>;
 
 /**
  * A mouse device is an input device that returns a tuple of two floats and four ints.
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 7b39a38c1..1619e5e4e 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
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d62b0efc2..d239ffbbd 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -35,7 +35,7 @@
 #include "core/settings.h"
 #include "input_common/keyboard.h"
 #include "input_common/main.h"
-#include "input_common/motion_emu.h"
+#include "input_common/mouse/mouse_input.h"
 #include "video_core/renderer_base.h"
 #include "video_core/video_core.h"
 #include "yuzu/bootmanager.h"
@@ -382,23 +382,19 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
 }
 
 void GRenderWindow::mousePressEvent(QMouseEvent* event) {
-    if (!Settings::values.touchscreen.enabled) {
-        input_subsystem->GetKeyboard()->PressKey(event->button());
-        return;
-    }
-
     // Touch input is handled in TouchBeginEvent
     if (event->source() == Qt::MouseEventSynthesizedBySystem) {
         return;
     }
 
     auto pos = event->pos();
+    const auto [x, y] = ScaleTouch(pos);
+    input_subsystem->GetMouse()->PressButton(x, y, event->button());
+
     if (event->button() == Qt::LeftButton) {
-        const auto [x, y] = ScaleTouch(pos);
         this->TouchPressed(x, y);
-    } else if (event->button() == Qt::RightButton) {
-        input_subsystem->GetMotionEmu()->BeginTilt(pos.x(), pos.y());
     }
+
     QWidget::mousePressEvent(event);
 }
 
@@ -410,26 +406,22 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
 
     auto pos = event->pos();
     const auto [x, y] = ScaleTouch(pos);
+    input_subsystem->GetMouse()->MouseMove(x, y);
     this->TouchMoved(x, y);
-    input_subsystem->GetMotionEmu()->Tilt(pos.x(), pos.y());
+
     QWidget::mouseMoveEvent(event);
 }
 
 void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
-    if (!Settings::values.touchscreen.enabled) {
-        input_subsystem->GetKeyboard()->ReleaseKey(event->button());
-        return;
-    }
-
     // Touch input is handled in TouchEndEvent
     if (event->source() == Qt::MouseEventSynthesizedBySystem) {
         return;
     }
 
+    input_subsystem->GetMouse()->ReleaseButton(event->button());
+
     if (event->button() == Qt::LeftButton) {
         this->TouchReleased();
-    } else if (event->button() == Qt::RightButton) {
-        input_subsystem->GetMotionEmu()->EndTilt();
     }
 }
 
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 72640f5e7..87440a013 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -19,6 +19,7 @@
 #include "core/hle/service/sm/sm.h"
 #include "input_common/gcadapter/gc_poller.h"
 #include "input_common/main.h"
+#include "input_common/mouse/mouse_poller.h"
 #include "input_common/udp/udp.h"
 #include "ui_configure_input_player.h"
 #include "yuzu/configuration/config.h"
@@ -186,6 +187,14 @@ QString ButtonToText(const Common::ParamPackage& param) {
         return {};
     }
 
+    if (param.Get("engine", "") == "mouse") {
+        if (param.Has("button")) {
+            const QString button_str = QString::number(int(param.Get("button", 0)));
+            return QObject::tr("Click %1").arg(button_str);
+        }
+        return GetKeyName(param.Get("code", 0));
+    }
+
     return QObject::tr("[unknown]");
 }
 
@@ -237,6 +246,26 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
 
         return {};
     }
+
+    if (param.Get("engine", "") == "mouse") {
+        if (dir == "modifier") {
+            return QObject::tr("[unused]");
+        }
+
+        if (dir == "left" || dir == "right") {
+            const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
+
+            return QObject::tr("Mouse %1").arg(axis_x_str);
+        }
+
+        if (dir == "up" || dir == "down") {
+            const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
+
+            return QObject::tr("Mouse %1").arg(axis_y_str);
+        }
+
+        return {};
+    }
     return QObject::tr("[unknown]");
 }
 } // namespace
@@ -532,6 +561,34 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
                 return;
             }
         }
+        if (input_subsystem->GetMouseButtons()->IsPolling()) {
+            params = input_subsystem->GetMouseButtons()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetMouseAnalogs()->IsPolling()) {
+            params = input_subsystem->GetMouseAnalogs()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetMouseMotions()->IsPolling()) {
+            params = input_subsystem->GetMouseMotions()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetMouseTouch()->IsPolling()) {
+            params = input_subsystem->GetMouseTouch()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
         for (auto& poller : device_pollers) {
             params = poller->GetNextInput();
             if (params.Has("engine") && IsInputAcceptable(params)) {
@@ -809,8 +866,9 @@ void ConfigureInputPlayer::UpdateUI() {
 
         int slider_value;
         auto& param = analogs_param[analog_id];
-        const bool is_controller =
-            param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad";
+        const bool is_controller = param.Get("engine", "") == "sdl" ||
+                                   param.Get("engine", "") == "gcpad" ||
+                                   param.Get("engine", "") == "mouse";
 
         if (is_controller) {
             if (!param.Has("deadzone")) {
@@ -1050,6 +1108,16 @@ void ConfigureInputPlayer::HandleClick(
         input_subsystem->GetUDPMotions()->BeginConfiguration();
     }
 
+    if (type == InputCommon::Polling::DeviceType::Button) {
+        input_subsystem->GetMouseButtons()->BeginConfiguration();
+    } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) {
+        input_subsystem->GetMouseAnalogs()->BeginConfiguration();
+    } else if (type == InputCommon::Polling::DeviceType::Motion) {
+        input_subsystem->GetMouseMotions()->BeginConfiguration();
+    } else {
+        input_subsystem->GetMouseTouch()->BeginConfiguration();
+    }
+
     timeout_timer->start(2500); // Cancel after 2.5 seconds
     poll_timer->start(50);      // Check for new inputs every 50ms
 }
@@ -1069,6 +1137,11 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
 
     input_subsystem->GetUDPMotions()->EndConfiguration();
 
+    input_subsystem->GetMouseButtons()->EndConfiguration();
+    input_subsystem->GetMouseAnalogs()->EndConfiguration();
+    input_subsystem->GetMouseMotions()->EndConfiguration();
+    input_subsystem->GetMouseTouch()->EndConfiguration();
+
     if (!abort) {
         (*input_setter)(params);
     }
@@ -1100,15 +1173,7 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
         return;
     }
 
-    if (want_keyboard_mouse) {
-        SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
-                         false);
-    } else {
-        // We don't want any mouse buttons, so don't stop polling
-        return;
-    }
-
-    SetPollingResult({}, true);
+    input_subsystem->GetMouse()->PressButton(0, 0, event->button());
 }
 
 void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 521209622..72ec25c99 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -9,7 +9,7 @@
 #include "core/perf_stats.h"
 #include "input_common/keyboard.h"
 #include "input_common/main.h"
-#include "input_common/motion_emu.h"
+#include "input_common/mouse/mouse_input.h"
 #include "input_common/sdl/sdl.h"
 #include "yuzu_cmd/emu_window/emu_window_sdl2.h"
 
@@ -30,7 +30,7 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
 
 void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
     TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
-    input_subsystem->GetMotionEmu()->Tilt(x, y);
+    input_subsystem->GetMouse()->MouseMove(x, y);
 }
 
 void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
@@ -42,9 +42,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
         }
     } else if (button == SDL_BUTTON_RIGHT) {
         if (state == SDL_PRESSED) {
-            input_subsystem->GetMotionEmu()->BeginTilt(x, y);
+            input_subsystem->GetMouse()->PressButton(x, y, button);
         } else {
-            input_subsystem->GetMotionEmu()->EndTilt();
+            input_subsystem->GetMouse()->ReleaseButton(button);
         }
     }
 }
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 5f35233b5..a103b04bd 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -17,7 +17,6 @@
 #include "core/settings.h"
 #include "input_common/keyboard.h"
 #include "input_common/main.h"
-#include "input_common/motion_emu.h"
 #include "video_core/renderer_base.h"
 #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"