From ff679f3d171ace12d1b3b68f305b1bb24b2130de Mon Sep 17 00:00:00 2001
From: german <german@thesoftwareartisans.com>
Date: Fri, 4 Sep 2020 21:48:03 -0500
Subject: [PATCH 1/7] Include HID and configuration changes related to motion

---
 src/core/frontend/input.h                     |  27 ++++
 src/core/hle/service/hid/controllers/npad.cpp | 117 ++++++++++++++++++
 src/core/hle/service/hid/controllers/npad.h   |  54 ++++++--
 src/core/hle/service/hid/hid.cpp              |  37 +++++-
 src/core/hle/service/hid/hid.h                |   2 +
 src/input_common/main.h                       |  10 +-
 src/input_common/settings.cpp                 |   7 ++
 src/input_common/settings.h                   |  17 +++
 src/yuzu/configuration/config.cpp             |  29 +++++
 src/yuzu/configuration/config.h               |   1 +
 .../configuration/configure_input_player.cpp  |  61 +++++++++
 .../configuration/configure_input_player.h    |   3 +
 .../configuration/configure_input_player.ui   |  99 +++++++++++++++
 13 files changed, 448 insertions(+), 16 deletions(-)

diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 2b098b7c6..6770475cf 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -136,6 +136,33 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>;
  */
 using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>;
 
+/**
+ * A real motion device is an input device that returns a tuple of accelerometer state vector,
+ * gyroscope state vector, rotation state vector and orientation state matrix.
+ *
+ * For both vectors:
+ *   x+ is the same direction as RIGHT on D-pad.
+ *   y+ is normal to the touch screen, pointing outward.
+ *   z+ is the same direction as UP on D-pad.
+ *
+ * For accelerometer state vector
+ *   Units: g (gravitational acceleration)
+ *
+ * For gyroscope state vector:
+ *   Orientation is determined by right-hand rule.
+ *   Units: deg/sec
+ *
+ * For rotation state vector
+ *   Units: rotations
+ *
+ * For orientation state matrix
+ *   x vector
+ *   y vector
+ *   z vector
+ */
+using RealMotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>,
+                                                Common::Vec3<float>, std::array<Common::Vec3f, 3>>>;
+
 /**
  * A touch device is an input device 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.
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e742497e1..acf748bf1 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -249,6 +249,9 @@ void Controller_NPad::OnLoadInputDevices() {
         std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
                        players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
                        sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
+        std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
+                       players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
+                       motions[i].begin(), Input::CreateDevice<Input::RealMotionDevice>);
     }
 }
 
@@ -265,6 +268,7 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
     auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
     const auto& button_state = buttons[controller_idx];
     const auto& analog_state = sticks[controller_idx];
+    const auto& motion_state = motions[controller_idx];
     const auto [stick_l_x_f, stick_l_y_f] =
         analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
     const auto [stick_r_x_f, stick_r_y_f] =
@@ -359,6 +363,45 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
             continue;
         }
         const u32 npad_index = static_cast<u32>(i);
+
+        const std::array<SixAxisGeneric*, 6> controller_sixaxes{
+            &npad.sixaxis_full,       &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
+            &npad.sixaxis_dual_right, &npad.sixaxis_left,     &npad.sixaxis_right,
+        };
+
+        for (auto* sixaxis_sensor : controller_sixaxes) {
+            sixaxis_sensor->common.entry_count = 16;
+            sixaxis_sensor->common.total_entry_count = 17;
+
+            const auto& last_entry =
+                sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+
+            sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
+            sixaxis_sensor->common.last_entry_index =
+                (sixaxis_sensor->common.last_entry_index + 1) % 17;
+
+            auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+
+            cur_entry.timestamp = last_entry.timestamp + 1;
+            cur_entry.timestamp2 = cur_entry.timestamp;
+        }
+
+        // Try to read sixaxis sensor states
+        std::array<MotionDevice, 2> motion_devices;
+
+        if (sixaxis_sensors_enabled) {
+            sixaxis_at_rest = true;
+            for (std::size_t e = 0; e < motion_devices.size(); ++e) {
+                const auto& device = motions[i][e];
+                if (device) {
+                    std::tie(motion_devices[e].accel, motion_devices[e].gyro,
+                             motion_devices[e].rotation, motion_devices[e].orientation) =
+                        device->GetStatus();
+                    sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 1.0f;
+                }
+            }
+        }
+
         RequestPadStateUpdate(npad_index);
         auto& pad_state = npad_pad_states[npad_index];
 
@@ -376,6 +419,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
 
         libnx_entry.connection_status.raw = 0;
         libnx_entry.connection_status.IsConnected.Assign(1);
+        auto& full_sixaxis_entry =
+            npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
+        auto& handheld_sixaxis_entry =
+            npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
+        auto& dual_left_sixaxis_entry =
+            npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
+        auto& dual_right_sixaxis_entry =
+            npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
+        auto& left_sixaxis_entry =
+            npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
+        auto& right_sixaxis_entry =
+            npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
 
         switch (controller_type) {
         case NPadControllerType::None:
@@ -390,6 +445,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
             main_controller.pad.r_stick = pad_state.r_stick;
 
             libnx_entry.connection_status.IsWired.Assign(1);
+
+            if (sixaxis_sensors_enabled && motions[i][0]) {
+                full_sixaxis_entry.accel = motion_devices[0].accel;
+                full_sixaxis_entry.gyro = motion_devices[0].gyro;
+                full_sixaxis_entry.rotation = motion_devices[0].rotation;
+                full_sixaxis_entry.orientation = motion_devices[0].orientation;
+            }
             break;
         case NPadControllerType::Handheld:
             handheld_entry.connection_status.raw = 0;
@@ -408,6 +470,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
             libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
             libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
             libnx_entry.connection_status.IsRightJoyWired.Assign(1);
+
+            if (sixaxis_sensors_enabled && motions[i][0]) {
+                handheld_sixaxis_entry.accel = motion_devices[0].accel;
+                handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
+                handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
+                handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
+            }
             break;
         case NPadControllerType::JoyDual:
             dual_entry.connection_status.raw = 0;
@@ -420,6 +489,32 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
 
             libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
             libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
+
+            if (sixaxis_sensors_enabled) {
+                if (motions[i][0] && motions[i][1]) {
+                    // set both
+                    dual_left_sixaxis_entry.accel = motion_devices[0].accel;
+                    dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
+                    dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
+                    dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
+                    dual_right_sixaxis_entry.accel = motion_devices[1].accel;
+                    dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
+                    dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
+                    dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
+                } else if (motions[i][0]) {
+                    // set right
+                    dual_right_sixaxis_entry.accel = motion_devices[0].accel;
+                    dual_right_sixaxis_entry.gyro = motion_devices[0].gyro;
+                    dual_right_sixaxis_entry.rotation = motion_devices[0].rotation;
+                    dual_right_sixaxis_entry.orientation = motion_devices[0].orientation;
+                } else if (motions[i][1]) {
+                    // set right
+                    dual_right_sixaxis_entry.accel = motion_devices[1].accel;
+                    dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
+                    dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
+                    dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
+                }
+            }
             break;
         case NPadControllerType::JoyLeft:
             left_entry.connection_status.raw = 0;
@@ -430,6 +525,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
             left_entry.pad.r_stick = pad_state.r_stick;
 
             libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
+
+            if (sixaxis_sensors_enabled && motions[i][0]) {
+                left_sixaxis_entry.accel = motion_devices[0].accel;
+                left_sixaxis_entry.gyro = motion_devices[0].gyro;
+                left_sixaxis_entry.rotation = motion_devices[0].rotation;
+                left_sixaxis_entry.orientation = motion_devices[0].orientation;
+            }
             break;
         case NPadControllerType::JoyRight:
             right_entry.connection_status.raw = 0;
@@ -440,6 +542,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
             right_entry.pad.r_stick = pad_state.r_stick;
 
             libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
+
+            if (sixaxis_sensors_enabled && motions[i][0]) {
+                right_sixaxis_entry.accel = motion_devices[0].accel;
+                right_sixaxis_entry.gyro = motion_devices[0].gyro;
+                right_sixaxis_entry.rotation = motion_devices[0].rotation;
+                right_sixaxis_entry.orientation = motion_devices[0].orientation;
+            }
             break;
         case NPadControllerType::Pokeball:
             pokeball_entry.connection_status.raw = 0;
@@ -574,6 +683,14 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo
     return gyroscope_zero_drift_mode;
 }
 
+bool Controller_NPad::IsSixAxisSensorAtRest() const {
+    return sixaxis_at_rest;
+}
+
+void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
+    sixaxis_sensors_enabled = six_axis_status;
+}
+
 void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
     const auto npad_index_1 = NPadIdToIndex(npad_id_1);
     const auto npad_index_2 = NPadIdToIndex(npad_id_2);
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ad25c6fbf..99d7e459a 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -126,6 +126,8 @@ public:
     void DisconnectNPad(u32 npad_id);
     void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
     GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
+    bool IsSixAxisSensorAtRest() const;
+    void SetSixAxisEnabled(bool six_axis_status);
     LedPattern GetLedPattern(u32 npad_id);
     void SetVibrationEnabled(bool can_vibrate);
     bool IsVibrationEnabled() const;
@@ -248,6 +250,24 @@ private:
     };
     static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
 
+    struct SixAxisStates {
+        s64_le timestamp{};
+        INSERT_PADDING_WORDS(2);
+        s64_le timestamp2{};
+        Common::Vec3f accel{};
+        Common::Vec3f gyro{};
+        Common::Vec3f rotation{};
+        std::array<Common::Vec3f, 3> orientation{};
+        s64_le always_one{1};
+    };
+    static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
+
+    struct SixAxisGeneric {
+        CommonHeader common{};
+        std::array<SixAxisStates, 17> sixaxis{};
+    };
+    static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
+
     enum class ColorReadError : u32_le {
         ReadOk = 0,
         ColorDoesntExist = 1,
@@ -277,6 +297,13 @@ private:
         };
     };
 
+    struct MotionDevice {
+        Common::Vec3f accel;
+        Common::Vec3f gyro{};
+        Common::Vec3f rotation;
+        std::array<Common::Vec3f, 3> orientation{};
+    };
+
     struct NPadEntry {
         NPadType joy_styles;
         NPadAssignments pad_assignment;
@@ -296,9 +323,12 @@ private:
         NPadGeneric pokeball_states;
         NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
                            // relying on this for the time being
-        INSERT_PADDING_BYTES(
-            0x708 *
-            6); // TODO(ogniK): SixAxis states, require more information before implementation
+        SixAxisGeneric sixaxis_full;
+        SixAxisGeneric sixaxis_handheld;
+        SixAxisGeneric sixaxis_dual_left;
+        SixAxisGeneric sixaxis_dual_right;
+        SixAxisGeneric sixaxis_left;
+        SixAxisGeneric sixaxis_right;
         NPadDevice device_type;
         NPadProperties properties;
         INSERT_PADDING_WORDS(1);
@@ -322,14 +352,18 @@ private:
 
     NPadType style{};
     std::array<NPadEntry, 10> shared_memory_entries{};
-    std::array<
+    using ButtonArray = std::array<
         std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
-        10>
-        buttons;
-    std::array<
+        10>;
+    using StickArray = std::array<
         std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
-        10>
-        sticks;
+        10>;
+    using MotionArray = std::array<std::array<std::unique_ptr<Input::RealMotionDevice>,
+                                              Settings::NativeMotion::NUM_MOTION_HID>,
+                                   10>;
+    ButtonArray buttons;
+    StickArray sticks;
+    MotionArray motions;
     std::vector<u32> supported_npad_id_types{};
     NpadHoldType hold_type{NpadHoldType::Vertical};
     // Each controller should have their own styleset changed event
@@ -338,6 +372,8 @@ private:
     std::array<ControllerHolder, 10> connected_controllers{};
     GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
     bool can_controllers_vibrate{true};
+    bool sixaxis_sensors_enabled{true};
+    bool sixaxis_at_rest{true};
     std::array<ControllerPad, 10> npad_pad_states{};
     bool is_in_lr_assignment_mode{false};
     Core::System& system;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index bd3c2f26b..302a25540 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -164,8 +164,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
         {56, nullptr, "ActivateJoyXpad"},
         {58, nullptr, "GetJoyXpadLifoHandle"},
         {59, nullptr, "GetJoyXpadIds"},
-        {60, nullptr, "ActivateSixAxisSensor"},
-        {61, nullptr, "DeactivateSixAxisSensor"},
+        {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
+        {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
         {62, nullptr, "GetSixAxisSensorLifoHandle"},
         {63, nullptr, "ActivateJoySixAxisSensor"},
         {64, nullptr, "DeactivateJoySixAxisSensor"},
@@ -329,6 +329,31 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
     rb.Push(0);
 }
 
+void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    const auto handle{rp.Pop<u32>()};
+    const auto applet_resource_user_id{rp.Pop<u64>()};
+    applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
+    LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
+              applet_resource_user_id);
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    const auto handle{rp.Pop<u32>()};
+    const auto applet_resource_user_id{rp.Pop<u64>()};
+    applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
+
+    LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
+              applet_resource_user_id);
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(RESULT_SUCCESS);
+}
+
 void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -484,13 +509,13 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
     const auto handle{rp.Pop<u32>()};
     const auto applet_resource_user_id{rp.Pop<u64>()};
 
-    LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
-                applet_resource_user_id);
+    LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
+              applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(RESULT_SUCCESS);
-    // TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
-    rb.Push(true);
+    rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+                .IsSixAxisSensorAtRest());
 }
 
 void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index efb07547f..e04aaf1e9 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -86,6 +86,8 @@ private:
     void CreateAppletResource(Kernel::HLERequestContext& ctx);
     void ActivateXpad(Kernel::HLERequestContext& ctx);
     void GetXpadIDs(Kernel::HLERequestContext& ctx);
+    void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
+    void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
     void ActivateDebugPad(Kernel::HLERequestContext& ctx);
     void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
     void ActivateMouse(Kernel::HLERequestContext& ctx);
diff --git a/src/input_common/main.h b/src/input_common/main.h
index f3fbf696e..18f44dcc3 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -21,10 +21,14 @@ namespace Settings::NativeButton {
 enum Values : int;
 }
 
+namespace Settings::NativeMotion {
+enum Values : int;
+}
+
 namespace InputCommon {
 namespace Polling {
 
-enum class DeviceType { Button, AnalogPreferred };
+enum class DeviceType { Button, AnalogPreferred, Motion };
 
 /**
  * A class that can be used to get inputs from an input device like controllers without having to
@@ -59,6 +63,7 @@ class MotionEmu;
  */
 using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
 using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
+using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>;
 
 class InputSubsystem {
 public:
@@ -103,6 +108,9 @@ public:
     /// Retrieves the button mappings for the given device.
     [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
 
+    /// Retrieves the motion mappings for the given device.
+    [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
+
     /// Retrieves the underlying GameCube analog handler.
     [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
 
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
index 80c719cf4..b66c05856 100644
--- a/src/input_common/settings.cpp
+++ b/src/input_common/settings.cpp
@@ -14,6 +14,13 @@ const std::array<const char*, NumButtons> mapping = {{
 }};
 }
 
+namespace NativeMotion {
+const std::array<const char*, NumMotions> mapping = {{
+    "motionleft",
+    "motionright",
+}};
+}
+
 namespace NativeAnalog {
 const std::array<const char*, NumAnalogs> mapping = {{
     "lstick",
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
index 2d258960b..ab0b95cf1 100644
--- a/src/input_common/settings.h
+++ b/src/input_common/settings.h
@@ -66,6 +66,21 @@ constexpr int NUM_STICKS_HID = NumAnalogs;
 extern const std::array<const char*, NumAnalogs> mapping;
 } // namespace NativeAnalog
 
+namespace NativeMotion {
+enum Values : int {
+    MOTIONLEFT,
+    MOTIONRIGHT,
+
+    NumMotions,
+};
+
+constexpr int MOTION_HID_BEGIN = MOTIONLEFT;
+constexpr int MOTION_HID_END = NumMotions;
+constexpr int NUM_MOTION_HID = NumMotions;
+
+extern const std::array<const char*, NumMotions> mapping;
+} // namespace NativeMotion
+
 namespace NativeMouseButton {
 enum Values {
     Left,
@@ -292,6 +307,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
 
 using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
 using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
+using MotionRaw = std::array<std::string, NativeMotion::NumMotions>;
 using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
 using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
 using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@@ -314,6 +330,7 @@ struct PlayerInput {
     ControllerType controller_type;
     ButtonsRaw buttons;
     AnalogsRaw analogs;
+    MotionRaw motions;
     std::string lstick_mod;
     std::string rstick_mod;
 
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 2bc55a26a..40ca42b75 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -36,6 +36,11 @@ const std::array<int, Settings::NativeButton::NumButtons> Config::default_button
     Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
 };
 
+const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
+    Qt::Key_7,
+    Qt::Key_8,
+};
+
 const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
     {
         Qt::Key_Up,
@@ -284,6 +289,22 @@ void Config::ReadPlayerValues() {
             }
         }
 
+        for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+            const std::string default_param =
+                InputCommon::GenerateKeyboardParam(default_motions[i]);
+            auto& player_motions = player.motions[i];
+
+            player_motions = qt_config
+                                 ->value(QStringLiteral("player_%1_").arg(p) +
+                                             QString::fromUtf8(Settings::NativeMotion::mapping[i]),
+                                         QString::fromStdString(default_param))
+                                 .toString()
+                                 .toStdString();
+            if (player_motions.empty()) {
+                player_motions = default_param;
+            }
+        }
+
         for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
             const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
                 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -922,6 +943,14 @@ void Config::SavePlayerValues() {
                          QString::fromStdString(player.buttons[i]),
                          QString::fromStdString(default_param));
         }
+        for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+            const std::string default_param =
+                InputCommon::GenerateKeyboardParam(default_motions[i]);
+            WriteSetting(QStringLiteral("player_%1_").arg(p) +
+                             QString::fromStdString(Settings::NativeMotion::mapping[i]),
+                         QString::fromStdString(player.motions[i]),
+                         QString::fromStdString(default_param));
+        }
         for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
             const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
                 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ca0d29c6c..5d8e45d78 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -23,6 +23,7 @@ public:
     void Save();
 
     static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
+    static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
     static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
     static const std::array<int, 2> default_stick_mod;
     static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 13ecb3dc5..f6942851a 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -262,6 +262,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
         },
     }};
 
+    motion_map = {
+        ui->buttonMotionLeft,
+        ui->buttonMotionRight,
+    };
+
     analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
     analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
     analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
@@ -304,6 +309,37 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
                              Config::default_buttons[button_id]);
     }
 
+    for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+        auto* const button = motion_map[motion_id];
+        if (button == nullptr) {
+            continue;
+        }
+
+        button->setContextMenuPolicy(Qt::CustomContextMenu);
+        connect(button, &QPushButton::clicked, [=, this] {
+            HandleClick(
+                motion_map[motion_id],
+                [=, this](Common::ParamPackage params) {
+                    motions_param[motion_id] = std::move(params);
+                },
+                InputCommon::Polling::DeviceType::Motion);
+        });
+        connect(button, &QPushButton::customContextMenuRequested,
+                [=, this](const QPoint& menu_location) {
+                    QMenu context_menu;
+                    context_menu.addAction(tr("Clear"), [&] {
+                        motions_param[motion_id].Clear();
+                        motion_map[motion_id]->setText(tr("[not set]"));
+                    });
+                    context_menu.addAction(tr("Restore Default"), [&] {
+                        motions_param[motion_id] = Common::ParamPackage{
+                            InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
+                        motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id]));
+                    });
+                    context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
+                });
+    }
+
     // Handle clicks for the modifier buttons as well.
     ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
     ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
@@ -448,6 +484,10 @@ void ConfigureInputPlayer::ApplyConfiguration() {
         return;
     }
 
+    auto& motions = player.motions;
+    std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
+                   [](const Common::ParamPackage& param) { return param.Serialize(); });
+
     player.controller_type =
         static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
     player.connected = ui->groupConnectedController->isChecked();
@@ -501,6 +541,8 @@ void ConfigureInputPlayer::LoadConfiguration() {
                        [](const std::string& str) { return Common::ParamPackage(str); });
         std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
                        [](const std::string& str) { return Common::ParamPackage(str); });
+        std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(),
+                       [](const std::string& str) { return Common::ParamPackage(str); });
     }
 
     UpdateUI();
@@ -544,6 +586,12 @@ void ConfigureInputPlayer::RestoreDefaults() {
             SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
         }
     }
+
+    for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+        motions_param[motion_id] = Common::ParamPackage{
+            InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
+    }
+
     UpdateUI();
     UpdateInputDevices();
     ui->comboControllerType->setCurrentIndex(0);
@@ -573,6 +621,15 @@ void ConfigureInputPlayer::ClearAll() {
         }
     }
 
+    for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+        const auto* const button = motion_map[motion_id];
+        if (button == nullptr || !button->isEnabled()) {
+            continue;
+        }
+
+        motions_param[motion_id].Clear();
+    }
+
     UpdateUI();
     UpdateInputDevices();
 }
@@ -582,6 +639,10 @@ void ConfigureInputPlayer::UpdateUI() {
         button_map[button]->setText(ButtonToText(buttons_param[button]));
     }
 
+    for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+        motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id]));
+    }
+
     ui->buttonLStickMod->setText(ButtonToText(lstick_mod));
     ui->buttonRStickMod->setText(ButtonToText(rstick_mod));
 
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index a25bc3bd9..a12216c55 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -128,11 +128,14 @@ private:
 
     std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
     std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
+    std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param;
 
     static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
 
     /// Each button input is represented by a QPushButton.
     std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
+    /// Each motion input is represented by a QPushButton.
+    std::array<QPushButton*, Settings::NativeMotion::NumMotions> motion_map;
     /// Extra buttons for the modifiers.
     Common::ParamPackage lstick_mod;
     Common::ParamPackage rstick_mod;
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 9bc681894..b0e572f37 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -2264,6 +2264,105 @@
                   </layout>
                  </widget>
                 </item>
+
+                 <item alignment="Qt::AlignHCenter">
+                   <widget class="QGroupBox" name="buttonMotionLeftGroup">
+                     <property name="title">
+                       <string>Motion left</string>
+                     </property>
+                     <property name="alignment">
+                       <set>Qt::AlignCenter</set>
+                     </property>
+                     <layout class="QVBoxLayout" name="buttonMotionLeftVerticalLayout">
+                       <property name="spacing">
+                         <number>3</number>
+                       </property>
+                       <property name="leftMargin">
+                         <number>3</number>
+                       </property>
+                       <property name="topMargin">
+                         <number>3</number>
+                       </property>
+                       <property name="rightMargin">
+                         <number>3</number>
+                       </property>
+                       <property name="bottomMargin">
+                         <number>3</number>
+                       </property>
+                       <item>
+                         <widget class="QPushButton" name="buttonMotionLeft">
+                           <property name="minimumSize">
+                             <size>
+                               <width>57</width>
+                               <height>0</height>
+                             </size>
+                           </property>
+                           <property name="maximumSize">
+                             <size>
+                               <width>55</width>
+                               <height>16777215</height>
+                             </size>
+                           </property>
+                           <property name="styleSheet">
+                             <string notr="true">min-width: 55px;</string>
+                           </property>
+                           <property name="text">
+                             <string>Motion left</string>
+                           </property>
+                         </widget>
+                       </item>
+                     </layout>
+                   </widget>
+                 </item>
+                 <item alignment="Qt::AlignHCenter">
+                   <widget class="QGroupBox" name="buttonMotionRightGroup">
+                     <property name="title">
+                       <string>Motion right</string>
+                     </property>
+                     <property name="alignment">
+                       <set>Qt::AlignCenter</set>
+                     </property>
+                     <layout class="QVBoxLayout" name="buttonMotionRightVerticalLayout">
+                       <property name="spacing">
+                         <number>3</number>
+                       </property>
+                       <property name="leftMargin">
+                         <number>3</number>
+                       </property>
+                       <property name="topMargin">
+                         <number>3</number>
+                       </property>
+                       <property name="rightMargin">
+                         <number>3</number>
+                       </property>
+                       <property name="bottomMargin">
+                         <number>3</number>
+                       </property>
+                       <item>
+                         <widget class="QPushButton" name="buttonMotionRight">
+                           <property name="minimumSize">
+                             <size>
+                               <width>57</width>
+                               <height>0</height>
+                             </size>
+                           </property>
+                           <property name="maximumSize">
+                             <size>
+                               <width>55</width>
+                               <height>16777215</height>
+                             </size>
+                           </property>
+                           <property name="styleSheet">
+                             <string notr="true">min-width: 55px;</string>
+                           </property>
+                           <property name="text">
+                             <string>Motion right</string>
+                           </property>
+                         </widget>
+                       </item>
+                     </layout>
+                   </widget>
+                 </item>
                </layout>
               </item>
               <item>

From df3cbd4758bed28d2bc33f7ba63485a3221e6070 Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Wed, 2 Sep 2020 12:18:41 -0400
Subject: [PATCH 2/7] controllers/npad: Simplify motion entry assignment

Simplifies the motion assignment in the Dual Joycon entry and assigns index 1 of the motion entry (Motion 2) for the right joycon.
---
 src/core/hle/service/hid/controllers/npad.cpp | 47 +++++++------------
 1 file changed, 18 insertions(+), 29 deletions(-)

diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index acf748bf1..9701318b5 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -490,30 +490,19 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
             libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
             libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
 
-            if (sixaxis_sensors_enabled) {
-                if (motions[i][0] && motions[i][1]) {
-                    // set both
-                    dual_left_sixaxis_entry.accel = motion_devices[0].accel;
-                    dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
-                    dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
-                    dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
-                    dual_right_sixaxis_entry.accel = motion_devices[1].accel;
-                    dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
-                    dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
-                    dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
-                } else if (motions[i][0]) {
-                    // set right
-                    dual_right_sixaxis_entry.accel = motion_devices[0].accel;
-                    dual_right_sixaxis_entry.gyro = motion_devices[0].gyro;
-                    dual_right_sixaxis_entry.rotation = motion_devices[0].rotation;
-                    dual_right_sixaxis_entry.orientation = motion_devices[0].orientation;
-                } else if (motions[i][1]) {
-                    // set right
-                    dual_right_sixaxis_entry.accel = motion_devices[1].accel;
-                    dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
-                    dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
-                    dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
-                }
+            if (sixaxis_sensors_enabled && motions[i][0]) {
+                // Set motion for the left joycon
+                dual_left_sixaxis_entry.accel = motion_devices[0].accel;
+                dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
+                dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
+                dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
+            }
+            if (sixaxis_sensors_enabled && motions[i][1]) {
+                // Set motion for the right joycon
+                dual_right_sixaxis_entry.accel = motion_devices[1].accel;
+                dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
+                dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
+                dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
             }
             break;
         case NPadControllerType::JoyLeft:
@@ -543,11 +532,11 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
 
             libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
 
-            if (sixaxis_sensors_enabled && motions[i][0]) {
-                right_sixaxis_entry.accel = motion_devices[0].accel;
-                right_sixaxis_entry.gyro = motion_devices[0].gyro;
-                right_sixaxis_entry.rotation = motion_devices[0].rotation;
-                right_sixaxis_entry.orientation = motion_devices[0].orientation;
+            if (sixaxis_sensors_enabled && motions[i][1]) {
+                right_sixaxis_entry.accel = motion_devices[1].accel;
+                right_sixaxis_entry.gyro = motion_devices[1].gyro;
+                right_sixaxis_entry.rotation = motion_devices[1].rotation;
+                right_sixaxis_entry.orientation = motion_devices[1].orientation;
             }
             break;
         case NPadControllerType::Pokeball:

From 8e18b61972880d75590e312a2aff91f7d7fdf91d Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Wed, 2 Sep 2020 12:33:37 -0400
Subject: [PATCH 3/7] configure_input_player: Show/hide motion buttons based on
 the controller

---
 .../configuration/configure_input_player.cpp  |  33 +++
 .../configuration/configure_input_player.h    |   3 +
 .../configuration/configure_input_player.ui   | 208 +++++++++---------
 3 files changed, 141 insertions(+), 103 deletions(-)

diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index f6942851a..7f4b794dc 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -421,9 +421,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
 
     UpdateControllerIcon();
     UpdateControllerAvailableButtons();
+    UpdateMotionButtons();
     connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
         UpdateControllerIcon();
         UpdateControllerAvailableButtons();
+        UpdateMotionButtons();
     });
 
     connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
@@ -893,6 +895,37 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
     }
 }
 
+void ConfigureInputPlayer::UpdateMotionButtons() {
+    if (debug) {
+        // Motion isn't used with the debug controller, hide both groupboxes.
+        ui->buttonMotionLeftGroup->hide();
+        ui->buttonMotionRightGroup->hide();
+        return;
+    }
+
+    // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
+    switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
+    case Settings::ControllerType::ProController:
+    case Settings::ControllerType::LeftJoycon:
+    case Settings::ControllerType::Handheld:
+        // Show "Motion 1" and hide "Motion 2".
+        ui->buttonMotionLeftGroup->show();
+        ui->buttonMotionRightGroup->hide();
+        break;
+    case Settings::ControllerType::RightJoycon:
+        // Show "Motion 2" and hide "Motion 1".
+        ui->buttonMotionLeftGroup->hide();
+        ui->buttonMotionRightGroup->show();
+        break;
+    case Settings::ControllerType::DualJoyconDetached:
+    default:
+        // Show both "Motion 1/2".
+        ui->buttonMotionLeftGroup->show();
+        ui->buttonMotionRightGroup->show();
+        break;
+    }
+}
+
 void ConfigureInputPlayer::showEvent(QShowEvent* event) {
     if (bottom_row == nullptr) {
         return;
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index a12216c55..b343f2c1d 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -107,6 +107,9 @@ private:
     /// Hides and disables controller settings based on the current controller type.
     void UpdateControllerAvailableButtons();
 
+    /// Shows or hides motion groupboxes based on the current controller type.
+    void UpdateMotionButtons();
+
     /// Gets the default controller mapping for this device and auto configures the input to match.
     void UpdateMappingWithDefaults();
 
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index b0e572f37..e03461d9d 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1983,6 +1983,9 @@
              <property name="spacing">
               <number>3</number>
              </property>
+             <property name="topMargin">
+              <number>0</number>
+             </property>
              <item>
               <spacer name="horizontalSpacerMiscButtons1">
                <property name="orientation">
@@ -1990,12 +1993,110 @@
                </property>
                <property name="sizeHint" stdset="0">
                 <size>
-                 <width>40</width>
-                 <height>0</height>
+                 <width>20</width>
+                 <height>20</height>
                 </size>
                </property>
               </spacer>
              </item>
+             <item>
+              <widget class="QGroupBox" name="buttonMotionLeftGroup">
+               <property name="title">
+                <string>Motion 1</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignCenter</set>
+               </property>
+               <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_2">
+                <property name="spacing">
+                 <number>3</number>
+                </property>
+                <property name="leftMargin">
+                 <number>3</number>
+                </property>
+                <property name="topMargin">
+                 <number>3</number>
+                </property>
+                <property name="rightMargin">
+                 <number>3</number>
+                </property>
+                <property name="bottomMargin">
+                 <number>3</number>
+                </property>
+                <item>
+                 <widget class="QPushButton" name="buttonMotionLeft">
+                  <property name="minimumSize">
+                   <size>
+                    <width>57</width>
+                    <height>0</height>
+                   </size>
+                  </property>
+                  <property name="maximumSize">
+                   <size>
+                    <width>55</width>
+                    <height>16777215</height>
+                   </size>
+                  </property>
+                  <property name="styleSheet">
+                   <string notr="true">min-width: 55px;</string>
+                  </property>
+                  <property name="text">
+                   <string>Left</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </widget>
+             </item>
+             <item>
+              <widget class="QGroupBox" name="buttonMotionRightGroup">
+               <property name="title">
+                <string>Motion 2</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignCenter</set>
+               </property>
+               <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_2">
+                <property name="spacing">
+                 <number>3</number>
+                </property>
+                <property name="leftMargin">
+                 <number>3</number>
+                </property>
+                <property name="topMargin">
+                 <number>3</number>
+                </property>
+                <property name="rightMargin">
+                 <number>3</number>
+                </property>
+                <property name="bottomMargin">
+                 <number>3</number>
+                </property>
+                <item>
+                 <widget class="QPushButton" name="buttonMotionRight">
+                  <property name="minimumSize">
+                   <size>
+                    <width>57</width>
+                    <height>0</height>
+                   </size>
+                  </property>
+                  <property name="maximumSize">
+                   <size>
+                    <width>55</width>
+                    <height>16777215</height>
+                   </size>
+                  </property>
+                  <property name="styleSheet">
+                   <string notr="true">min-width: 55px;</string>
+                  </property>
+                  <property name="text">
+                   <string>Right</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </widget>
+             </item>
              <item>
               <spacer name="horizontalSpacerMiscButtons4">
                <property name="orientation">
@@ -2003,8 +2104,8 @@
                </property>
                <property name="sizeHint" stdset="0">
                 <size>
-                 <width>40</width>
-                 <height>0</height>
+                 <width>20</width>
+                 <height>20</height>
                 </size>
                </property>
               </spacer>
@@ -2264,105 +2365,6 @@
                   </layout>
                  </widget>
                 </item>
-
-                 <item alignment="Qt::AlignHCenter">
-                   <widget class="QGroupBox" name="buttonMotionLeftGroup">
-                     <property name="title">
-                       <string>Motion left</string>
-                     </property>
-                     <property name="alignment">
-                       <set>Qt::AlignCenter</set>
-                     </property>
-                     <layout class="QVBoxLayout" name="buttonMotionLeftVerticalLayout">
-                       <property name="spacing">
-                         <number>3</number>
-                       </property>
-                       <property name="leftMargin">
-                         <number>3</number>
-                       </property>
-                       <property name="topMargin">
-                         <number>3</number>
-                       </property>
-                       <property name="rightMargin">
-                         <number>3</number>
-                       </property>
-                       <property name="bottomMargin">
-                         <number>3</number>
-                       </property>
-                       <item>
-                         <widget class="QPushButton" name="buttonMotionLeft">
-                           <property name="minimumSize">
-                             <size>
-                               <width>57</width>
-                               <height>0</height>
-                             </size>
-                           </property>
-                           <property name="maximumSize">
-                             <size>
-                               <width>55</width>
-                               <height>16777215</height>
-                             </size>
-                           </property>
-                           <property name="styleSheet">
-                             <string notr="true">min-width: 55px;</string>
-                           </property>
-                           <property name="text">
-                             <string>Motion left</string>
-                           </property>
-                         </widget>
-                       </item>
-                     </layout>
-                   </widget>
-                 </item>
-                 <item alignment="Qt::AlignHCenter">
-                   <widget class="QGroupBox" name="buttonMotionRightGroup">
-                     <property name="title">
-                       <string>Motion right</string>
-                     </property>
-                     <property name="alignment">
-                       <set>Qt::AlignCenter</set>
-                     </property>
-                     <layout class="QVBoxLayout" name="buttonMotionRightVerticalLayout">
-                       <property name="spacing">
-                         <number>3</number>
-                       </property>
-                       <property name="leftMargin">
-                         <number>3</number>
-                       </property>
-                       <property name="topMargin">
-                         <number>3</number>
-                       </property>
-                       <property name="rightMargin">
-                         <number>3</number>
-                       </property>
-                       <property name="bottomMargin">
-                         <number>3</number>
-                       </property>
-                       <item>
-                         <widget class="QPushButton" name="buttonMotionRight">
-                           <property name="minimumSize">
-                             <size>
-                               <width>57</width>
-                               <height>0</height>
-                             </size>
-                           </property>
-                           <property name="maximumSize">
-                             <size>
-                               <width>55</width>
-                               <height>16777215</height>
-                             </size>
-                           </property>
-                           <property name="styleSheet">
-                             <string notr="true">min-width: 55px;</string>
-                           </property>
-                           <property name="text">
-                             <string>Motion right</string>
-                           </property>
-                         </widget>
-                       </item>
-                     </layout>
-                   </widget>
-                 </item>
                </layout>
               </item>
               <item>

From 0774b17846fc7bd12bfe329fbaed6524d96c81cb Mon Sep 17 00:00:00 2001
From: german <german@thesoftwareartisans.com>
Date: Wed, 2 Sep 2020 19:59:34 -0500
Subject: [PATCH 4/7] Remove RealMotionDevice

---
 src/core/frontend/input.h                     | 29 +++++--------------
 src/core/hle/service/hid/controllers/npad.cpp |  5 ++--
 src/core/hle/service/hid/controllers/npad.h   | 10 +++----
 src/input_common/motion_emu.cpp               | 17 ++++++++---
 src/input_common/udp/client.cpp               | 10 ++++++-
 src/input_common/udp/client.h                 |  3 +-
 src/input_common/udp/udp.cpp                  |  2 +-
 7 files changed, 41 insertions(+), 35 deletions(-)

diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 6770475cf..9da0d2829 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -119,25 +119,7 @@ using ButtonDevice = InputDevice<bool>;
 using AnalogDevice = InputDevice<std::tuple<float, float>>;
 
 /**
- * A motion device is an input device that returns a tuple of accelerometer state vector and
- * gyroscope state vector.
- *
- * For both vectors:
- *   x+ is the same direction as LEFT on D-pad.
- *   y+ is normal to the touch screen, pointing outward.
- *   z+ is the same direction as UP on D-pad.
- *
- * For accelerometer state vector
- *   Units: g (gravitational acceleration)
- *
- * For gyroscope state vector:
- *   Orientation is determined by right-hand rule.
- *   Units: deg/sec
- */
-using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>;
-
-/**
- * A real motion device is an input device that returns a tuple of accelerometer state vector,
+ * A motion status is an object that returns a tuple of accelerometer state vector,
  * gyroscope state vector, rotation state vector and orientation state matrix.
  *
  * For both vectors:
@@ -160,8 +142,13 @@ using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<fl
  *   y vector
  *   z vector
  */
-using RealMotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>,
-                                                Common::Vec3<float>, std::array<Common::Vec3f, 3>>>;
+using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
+                                std::array<Common::Vec3f, 3>>;
+
+/**
+ * A motion device is an input device that returns a motion status object
+ */
+using MotionDevice = InputDevice<MotionStatus>;
 
 /**
  * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 9701318b5..2e06372a4 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -251,7 +251,7 @@ void Controller_NPad::OnLoadInputDevices() {
                        sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
         std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
                        players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
-                       motions[i].begin(), Input::CreateDevice<Input::RealMotionDevice>);
+                       motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
     }
 }
 
@@ -397,7 +397,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
                     std::tie(motion_devices[e].accel, motion_devices[e].gyro,
                              motion_devices[e].rotation, motion_devices[e].orientation) =
                         device->GetStatus();
-                    sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 1.0f;
+                    sixaxis_at_rest =
+                        sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.00005f;
                 }
             }
         }
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 99d7e459a..7b07d2e8b 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -299,9 +299,9 @@ private:
 
     struct MotionDevice {
         Common::Vec3f accel;
-        Common::Vec3f gyro{};
+        Common::Vec3f gyro;
         Common::Vec3f rotation;
-        std::array<Common::Vec3f, 3> orientation{};
+        std::array<Common::Vec3f, 3> orientation;
     };
 
     struct NPadEntry {
@@ -358,9 +358,9 @@ private:
     using StickArray = std::array<
         std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
         10>;
-    using MotionArray = std::array<std::array<std::unique_ptr<Input::RealMotionDevice>,
-                                              Settings::NativeMotion::NUM_MOTION_HID>,
-                                   10>;
+    using MotionArray = std::array<
+        std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>,
+        10>;
     ButtonArray buttons;
     StickArray sticks;
     MotionArray motions;
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
index d4cdf76a3..69fd3c1d2 100644
--- a/src/input_common/motion_emu.cpp
+++ b/src/input_common/motion_emu.cpp
@@ -56,7 +56,7 @@ public:
         is_tilting = false;
     }
 
-    std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() {
+    Input::MotionStatus GetStatus() {
         std::lock_guard guard{status_mutex};
         return status;
     }
@@ -76,7 +76,7 @@ private:
 
     Common::Event shutdown_event;
 
-    std::tuple<Common::Vec3<float>, Common::Vec3<float>> status;
+    Input::MotionStatus status;
     std::mutex status_mutex;
 
     // Note: always keep the thread declaration at the end so that other objects are initialized
@@ -113,10 +113,19 @@ private:
             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);
+                status = std::make_tuple(gravity, angular_rate, rotation, orientation);
             }
         }
     }
@@ -131,7 +140,7 @@ public:
         device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
     }
 
-    std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override {
+    Input::MotionStatus GetStatus() const override {
         return device->GetStatus();
     }
 
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 3f4eaf448..91e13482d 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -170,10 +170,18 @@ void Client::OnPadData(Response::PadData data) {
     // directions correspond to the ones of the Switch
     Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z);
     Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll);
+
+    // TODO: Calculate the correct rotation vector and orientation matrix
+    const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f);
+    const std::array orientation{
+        Common::Vec3f(1.0f, 0.0f, 0.0f),
+        Common::Vec3f(0.0f, 1.0f, 0.0f),
+        Common::Vec3f(0.0f, 0.0f, 1.0f),
+    };
     {
         std::lock_guard guard(status->update_mutex);
 
-        status->motion_status = {accel, gyro};
+        status->motion_status = {accel, gyro, rotation, orientation};
 
         // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
         // between a simple "tap" and a hard press that causes the touch screen to click.
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index b8c654755..a73283ae8 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -14,6 +14,7 @@
 #include "common/common_types.h"
 #include "common/thread.h"
 #include "common/vector_math.h"
+#include "core/frontend/input.h"
 
 namespace InputCommon::CemuhookUDP {
 
@@ -30,7 +31,7 @@ struct Version;
 
 struct DeviceStatus {
     std::mutex update_mutex;
-    std::tuple<Common::Vec3<float>, Common::Vec3<float>> motion_status;
+    Input::MotionStatus motion_status;
     std::tuple<float, float, bool> touch_status;
 
     // calibration data for scaling the device's touch area to 3ds
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 4b347e47e..03bae5752 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -29,7 +29,7 @@ private:
 class UDPMotionDevice final : public Input::MotionDevice {
 public:
     explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
-    std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override {
+    Input::MotionStatus GetStatus() const override {
         std::lock_guard guard(status->update_mutex);
         return status->motion_status;
     }

From 6ee8eab670acfed494ade355d77a32c57f7c9585 Mon Sep 17 00:00:00 2001
From: german <german@thesoftwareartisans.com>
Date: Fri, 4 Sep 2020 21:35:42 -0500
Subject: [PATCH 5/7] Add cemu hook changes related to PR #4609

---
 src/core/hle/service/hid/controllers/npad.cpp |   3 +-
 src/input_common/main.cpp                     |  46 ++++-
 src/input_common/main.h                       |  14 ++
 src/input_common/udp/client.cpp               | 185 ++++++++++++++----
 src/input_common/udp/client.h                 |  74 ++++++-
 src/input_common/udp/udp.cpp                  | 175 ++++++++++-------
 src/input_common/udp/udp.h                    |  61 ++++--
 .../configuration/configure_input_player.cpp  |  22 +++
 8 files changed, 445 insertions(+), 135 deletions(-)

diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 2e06372a4..510fa3071 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -397,8 +397,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
                     std::tie(motion_devices[e].accel, motion_devices[e].gyro,
                              motion_devices[e].rotation, motion_devices[e].orientation) =
                         device->GetStatus();
-                    sixaxis_at_rest =
-                        sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.00005f;
+                    sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
                 }
             }
         }
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index ea1a1cee6..062ec66b5 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -12,6 +12,7 @@
 #include "input_common/main.h"
 #include "input_common/motion_emu.h"
 #include "input_common/touch_from_button.h"
+#include "input_common/udp/client.h"
 #include "input_common/udp/udp.h"
 #ifdef HAVE_SDL2
 #include "input_common/sdl/sdl.h"
@@ -40,7 +41,11 @@ struct InputSubsystem::Impl {
         sdl = SDL::Init();
 #endif
 
-        udp = CemuhookUDP::Init();
+        udp = std::make_shared<InputCommon::CemuhookUDP::Client>();
+        udpmotion = std::make_shared<UDPMotionFactory>(udp);
+        Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
+        udptouch = std::make_shared<UDPTouchFactory>(udp);
+        Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
     }
 
     void Shutdown() {
@@ -53,12 +58,17 @@ struct InputSubsystem::Impl {
 #ifdef HAVE_SDL2
         sdl.reset();
 #endif
-        udp.reset();
         Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
         Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
 
         gcbuttons.reset();
         gcanalog.reset();
+
+        Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
+        Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
+
+        udpmotion.reset();
+        udptouch.reset();
     }
 
     [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
@@ -109,14 +119,28 @@ struct InputSubsystem::Impl {
         return {};
     }
 
+    [[nodiscard]] MotionMapping GetMotionMappingForDevice(
+        const Common::ParamPackage& params) const {
+        if (!params.Has("class") || params.Get("class", "") == "any") {
+            return {};
+        }
+        if (params.Get("class", "") == "cemuhookudp") {
+            // TODO return the correct motion device
+            return {};
+        }
+        return {};
+    }
+
     std::shared_ptr<Keyboard> keyboard;
     std::shared_ptr<MotionEmu> motion_emu;
 #ifdef HAVE_SDL2
     std::unique_ptr<SDL::State> sdl;
 #endif
-    std::unique_ptr<CemuhookUDP::State> udp;
     std::shared_ptr<GCButtonFactory> gcbuttons;
     std::shared_ptr<GCAnalogFactory> gcanalog;
+    std::shared_ptr<UDPMotionFactory> udpmotion;
+    std::shared_ptr<UDPTouchFactory> udptouch;
+    std::shared_ptr<CemuhookUDP::Client> udp;
 };
 
 InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -175,6 +199,22 @@ const GCButtonFactory* InputSubsystem::GetGCButtons() const {
     return impl->gcbuttons.get();
 }
 
+UDPMotionFactory* InputSubsystem::GetUDPMotions() {
+    return impl->udpmotion.get();
+}
+
+const UDPMotionFactory* InputSubsystem::GetUDPMotions() const {
+    return impl->udpmotion.get();
+}
+
+UDPTouchFactory* InputSubsystem::GetUDPTouch() {
+    return impl->udptouch.get();
+}
+
+const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
+    return impl->udptouch.get();
+}
+
 void InputSubsystem::ReloadInputDevices() {
     if (!impl->udp) {
         return;
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 18f44dcc3..dded3f1ef 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -54,6 +54,8 @@ public:
 
 class GCAnalogFactory;
 class GCButtonFactory;
+class UDPMotionFactory;
+class UDPTouchFactory;
 class Keyboard;
 class MotionEmu;
 
@@ -123,6 +125,18 @@ public:
     /// Retrieves the underlying GameCube button handler.
     [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
 
+    /// Retrieves the underlying udp motion handler.
+    [[nodiscard]] UDPMotionFactory* GetUDPMotions();
+
+    /// Retrieves the underlying udp motion handler.
+    [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const;
+
+    /// Retrieves the underlying udp touch handler.
+    [[nodiscard]] UDPTouchFactory* GetUDPTouch();
+
+    /// Retrieves the underlying udp touch handler.
+    [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
+
     /// Reloads the input devices
     void ReloadInputDevices();
 
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 91e13482d..e0c796040 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -2,14 +2,13 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
-#include <algorithm>
-#include <array>
 #include <chrono>
 #include <cstring>
 #include <functional>
 #include <thread>
 #include <boost/asio.hpp>
 #include "common/logging/log.h"
+#include "core/settings.h"
 #include "input_common/udp/client.h"
 #include "input_common/udp/protocol.h"
 
@@ -131,21 +130,60 @@ static void SocketLoop(Socket* socket) {
     socket->Loop();
 }
 
-Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port,
-               u8 pad_index, u32 client_id)
-    : status(std::move(status)) {
-    StartCommunication(host, port, pad_index, client_id);
+Client::Client() {
+    LOG_INFO(Input, "Udp Initialization started");
+    for (std::size_t client = 0; client < clients.size(); client++) {
+        u8 pad = client % 4;
+        StartCommunication(client, Settings::values.udp_input_address,
+                           Settings::values.udp_input_port, pad, 24872);
+        // Set motion parameters
+        // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
+        // Real HW values are unkown, 0.0001 is an aproximate to Standard
+        clients[client].motion.SetGyroThreshold(0.0001f);
+    }
 }
 
 Client::~Client() {
-    socket->Stop();
-    thread.join();
+    Reset();
 }
 
+std::vector<Common::ParamPackage> Client::GetInputDevices() const {
+    std::vector<Common::ParamPackage> devices;
+    for (std::size_t client = 0; client < clients.size(); client++) {
+        if (!DeviceConnected(client)) {
+            continue;
+        }
+        std::string name = fmt::format("UDP Controller{} {} {}", clients[client].active,
+                                       clients[client].active == 1, client);
+        devices.emplace_back(Common::ParamPackage{
+            {"class", "cemuhookudp"},
+            {"display", std::move(name)},
+            {"port", std::to_string(client)},
+        });
+    }
+    return devices;
+}
+
+bool Client::DeviceConnected(std::size_t pad) const {
+    // Use last timestamp to detect if the socket has stopped sending data
+    const auto now = std::chrono::system_clock::now();
+    u64 time_difference =
+        std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update)
+            .count();
+    return time_difference < 1000 && clients[pad].active == 1;
+}
+
+void Client::ReloadUDPClient() {
+    for (std::size_t client = 0; client < clients.size(); client++) {
+        ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client);
+    }
+}
 void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) {
-    socket->Stop();
-    thread.join();
-    StartCommunication(host, port, pad_index, client_id);
+    // client number must be determined from host / port and pad index
+    std::size_t client = pad_index;
+    clients[client].socket->Stop();
+    clients[client].thread.join();
+    StartCommunication(client, host, port, pad_index, client_id);
 }
 
 void Client::OnVersion(Response::Version data) {
@@ -157,31 +195,39 @@ void Client::OnPortInfo(Response::PortInfo data) {
 }
 
 void Client::OnPadData(Response::PadData data) {
+    // client number must be determined from host / port and pad index
+    std::size_t client = data.info.id;
     LOG_TRACE(Input, "PadData packet received");
-    if (data.packet_counter <= packet_sequence) {
+    if (data.packet_counter == clients[client].packet_sequence) {
         LOG_WARNING(
             Input,
             "PadData packet dropped because its stale info. Current count: {} Packet count: {}",
-            packet_sequence, data.packet_counter);
+            clients[client].packet_sequence, data.packet_counter);
         return;
     }
-    packet_sequence = data.packet_counter;
-    // TODO: Check how the Switch handles motions and how the CemuhookUDP motion
-    // directions correspond to the ones of the Switch
-    Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z);
-    Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll);
+    clients[client].active = data.info.is_pad_active;
+    clients[client].packet_sequence = data.packet_counter;
+    const auto now = std::chrono::system_clock::now();
+    u64 time_difference = std::chrono::duration_cast<std::chrono::microseconds>(
+                              now - clients[client].last_motion_update)
+                              .count();
+    clients[client].last_motion_update = now;
+    Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
+    clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
+    // Gyroscope values are not it the correct scale from better joy.
+    // By dividing by 312 allow us to make one full turn = 1 turn
+    // This must be a configurable valued called sensitivity
+    clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f);
+    clients[client].motion.UpdateRotation(time_difference);
+    clients[client].motion.UpdateOrientation(time_difference);
+    Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
+    Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
+    Common::Vec3f rotation = clients[client].motion.GetRotations();
+    std::array<Common::Vec3f, 3> orientation = clients[client].motion.GetOrientation();
 
-    // TODO: Calculate the correct rotation vector and orientation matrix
-    const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f);
-    const std::array orientation{
-        Common::Vec3f(1.0f, 0.0f, 0.0f),
-        Common::Vec3f(0.0f, 1.0f, 0.0f),
-        Common::Vec3f(0.0f, 0.0f, 1.0f),
-    };
     {
-        std::lock_guard guard(status->update_mutex);
-
-        status->motion_status = {accel, gyro, rotation, orientation};
+        std::lock_guard guard(clients[client].status.update_mutex);
+        clients[client].status.motion_status = {accelerometer, gyroscope, rotation, orientation};
 
         // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
         // between a simple "tap" and a hard press that causes the touch screen to click.
@@ -190,11 +236,11 @@ void Client::OnPadData(Response::PadData data) {
         float x = 0;
         float y = 0;
 
-        if (is_active && status->touch_calibration) {
-            const u16 min_x = status->touch_calibration->min_x;
-            const u16 max_x = status->touch_calibration->max_x;
-            const u16 min_y = status->touch_calibration->min_y;
-            const u16 max_y = status->touch_calibration->max_y;
+        if (is_active && clients[client].status.touch_calibration) {
+            const u16 min_x = clients[client].status.touch_calibration->min_x;
+            const u16 max_x = clients[client].status.touch_calibration->max_x;
+            const u16 min_y = clients[client].status.touch_calibration->min_y;
+            const u16 max_y = clients[client].status.touch_calibration->max_y;
 
             x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) /
                 static_cast<float>(max_x - min_x);
@@ -202,17 +248,82 @@ void Client::OnPadData(Response::PadData data) {
                 static_cast<float>(max_y - min_y);
         }
 
-        status->touch_status = {x, y, is_active};
+        clients[client].status.touch_status = {x, y, is_active};
+
+        if (configuring) {
+            UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
+        }
     }
 }
 
-void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) {
+void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index,
+                                u32 client_id) {
     SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
                             [this](Response::PortInfo info) { OnPortInfo(info); },
                             [this](Response::PadData data) { OnPadData(data); }};
     LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
-    socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
-    thread = std::thread{SocketLoop, this->socket.get()};
+    clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
+    clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
+}
+
+void Client::Reset() {
+    for (std::size_t client = 0; client < clients.size(); client++) {
+        clients[client].socket->Stop();
+        clients[client].thread.join();
+    }
+}
+
+void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
+                                const Common::Vec3<float>& gyro, bool touch) {
+    if (configuring) {
+        UDPPadStatus pad;
+        if (touch) {
+            pad.touch = PadTouch::Click;
+            pad_queue[client].Push(pad);
+        }
+        for (size_t i = 0; i < 3; ++i) {
+            if (gyro[i] > 6.0f || gyro[i] < -6.0f) {
+                pad.motion = static_cast<PadMotion>(i);
+                pad.motion_value = gyro[i];
+                pad_queue[client].Push(pad);
+            }
+            if (acc[i] > 2.0f || acc[i] < -2.0f) {
+                pad.motion = static_cast<PadMotion>(i + 3);
+                pad.motion_value = acc[i];
+                pad_queue[client].Push(pad);
+            }
+        }
+    }
+}
+
+void Client::BeginConfiguration() {
+    for (auto& pq : pad_queue) {
+        pq.Clear();
+    }
+    configuring = true;
+}
+
+void Client::EndConfiguration() {
+    for (auto& pq : pad_queue) {
+        pq.Clear();
+    }
+    configuring = false;
+}
+
+DeviceStatus& Client::GetPadState(std::size_t pad) {
+    return clients[pad].status;
+}
+
+const DeviceStatus& Client::GetPadState(std::size_t pad) const {
+    return clients[pad].status;
+}
+
+std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() {
+    return pad_queue;
+}
+
+const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const {
+    return pad_queue;
 }
 
 void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id,
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index a73283ae8..523dc6a7a 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -12,9 +12,12 @@
 #include <thread>
 #include <tuple>
 #include "common/common_types.h"
+#include "common/param_package.h"
 #include "common/thread.h"
+#include "common/threadsafe_queue.h"
 #include "common/vector_math.h"
 #include "core/frontend/input.h"
+#include "input_common/motion_input.h"
 
 namespace InputCommon::CemuhookUDP {
 
@@ -29,6 +32,27 @@ struct PortInfo;
 struct Version;
 } // namespace Response
 
+enum class PadMotion {
+    GyroX,
+    GyroY,
+    GyroZ,
+    AccX,
+    AccY,
+    AccZ,
+    Undefined,
+};
+
+enum class PadTouch {
+    Click,
+    Undefined,
+};
+
+struct UDPPadStatus {
+    PadTouch touch{PadTouch::Undefined};
+    PadMotion motion{PadMotion::Undefined};
+    f32 motion_value{0.0f};
+};
+
 struct DeviceStatus {
     std::mutex update_mutex;
     Input::MotionStatus motion_status;
@@ -46,22 +70,58 @@ struct DeviceStatus {
 
 class Client {
 public:
-    explicit Client(std::shared_ptr<DeviceStatus> status, const std::string& host = DEFAULT_ADDR,
-                    u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872);
+    // Initialize the UDP client capture and read sequence
+    Client();
+
+    // Close and release the client
     ~Client();
+
+    // Used for polling
+    void BeginConfiguration();
+    void EndConfiguration();
+
+    std::vector<Common::ParamPackage> GetInputDevices() const;
+
+    bool DeviceConnected(std::size_t pad) const;
+    void ReloadUDPClient();
     void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0,
                       u32 client_id = 24872);
 
+    std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue();
+    const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const;
+
+    DeviceStatus& GetPadState(std::size_t pad);
+    const DeviceStatus& GetPadState(std::size_t pad) const;
+
 private:
+    struct ClientData {
+        std::unique_ptr<Socket> socket;
+        DeviceStatus status;
+        std::thread thread;
+        u64 packet_sequence = 0;
+        u8 active;
+
+        // Realtime values
+        // motion is initalized with PID values for drift correction on joycons
+        InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
+        std::chrono::time_point<std::chrono::system_clock> last_motion_update;
+    };
+
+    // For shutting down, clear all data, join all threads, release usb
+    void Reset();
+
     void OnVersion(Response::Version);
     void OnPortInfo(Response::PortInfo);
     void OnPadData(Response::PadData);
-    void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id);
+    void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index,
+                            u32 client_id);
+    void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
+                            const Common::Vec3<float>& gyro, bool touch);
 
-    std::unique_ptr<Socket> socket;
-    std::shared_ptr<DeviceStatus> status;
-    std::thread thread;
-    u64 packet_sequence = 0;
+    bool configuring = false;
+
+    std::array<ClientData, 4> clients;
+    std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue;
 };
 
 /// An async job allowing configuration of the touchpad calibration.
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 03bae5752..eba077a36 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -1,105 +1,144 @@
-// Copyright 2018 Citra Emulator Project
+// 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 <optional>
-#include <tuple>
-
-#include "common/param_package.h"
-#include "core/frontend/input.h"
-#include "core/settings.h"
+#include <utility>
+#include "common/assert.h"
+#include "common/threadsafe_queue.h"
 #include "input_common/udp/client.h"
 #include "input_common/udp/udp.h"
 
-namespace InputCommon::CemuhookUDP {
+namespace InputCommon {
 
-class UDPTouchDevice final : public Input::TouchDevice {
+class UDPMotion final : public Input::MotionDevice {
 public:
-    explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
-    std::tuple<float, float, bool> GetStatus() const override {
-        std::lock_guard guard(status->update_mutex);
-        return status->touch_status;
-    }
+    UDPMotion(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_)
+        : ip(ip_), port(port_), pad(pad_), client(client_) {}
 
-private:
-    std::shared_ptr<DeviceStatus> status;
-};
-
-class UDPMotionDevice final : public Input::MotionDevice {
-public:
-    explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
     Input::MotionStatus GetStatus() const override {
-        std::lock_guard guard(status->update_mutex);
-        return status->motion_status;
+        return client->GetPadState(pad).motion_status;
     }
 
 private:
-    std::shared_ptr<DeviceStatus> status;
+    const std::string ip;
+    const int port;
+    const int pad;
+    CemuhookUDP::Client* client;
+    mutable std::mutex mutex;
 };
 
-class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
-public:
-    explicit UDPTouchFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
+/// A motion device factory that creates motion devices from JC Adapter
+UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
+    : client(std::move(client_)) {}
 
-    std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override {
-        {
-            std::lock_guard guard(status->update_mutex);
-            status->touch_calibration = DeviceStatus::CalibrationData{};
-            // These default values work well for DS4 but probably not other touch inputs
-            status->touch_calibration->min_x = params.Get("min_x", 100);
-            status->touch_calibration->min_y = params.Get("min_y", 50);
-            status->touch_calibration->max_x = params.Get("max_x", 1800);
-            status->touch_calibration->max_y = params.Get("max_y", 850);
+/**
+ * Creates motion device
+ * @param params contains parameters for creating the device:
+ *     - "port": the nth jcpad on the adapter
+ */
+std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
+    const std::string ip = params.Get("ip", "127.0.0.1");
+    const int port = params.Get("port", 26760);
+    const int pad = params.Get("pad_index", 0);
+
+    return std::make_unique<UDPMotion>(ip, port, pad, client.get());
+}
+
+void UDPMotionFactory::BeginConfiguration() {
+    polling = true;
+    client->BeginConfiguration();
+}
+
+void UDPMotionFactory::EndConfiguration() {
+    polling = false;
+    client->EndConfiguration();
+}
+
+Common::ParamPackage UDPMotionFactory::GetNextInput() {
+    Common::ParamPackage params;
+    CemuhookUDP::UDPPadStatus pad;
+    auto& queue = client->GetPadQueue();
+    for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
+        while (queue[pad_number].Pop(pad)) {
+            if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
+                continue;
+            }
+            params.Set("engine", "cemuhookudp");
+            params.Set("ip", "127.0.0.1");
+            params.Set("port", 26760);
+            params.Set("pad_index", static_cast<int>(pad_number));
+            params.Set("motion", static_cast<u16>(pad.motion));
+            return params;
         }
-        return std::make_unique<UDPTouchDevice>(status);
     }
+    return params;
+}
 
-private:
-    std::shared_ptr<DeviceStatus> status;
-};
-
-class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
+class UDPTouch final : public Input::TouchDevice {
 public:
-    explicit UDPMotionFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
+    UDPTouch(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_)
+        : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
 
-    std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
-        return std::make_unique<UDPMotionDevice>(status);
+    std::tuple<float, float, bool> GetStatus() const override {
+        return client->GetPadState(pad).touch_status;
     }
 
 private:
-    std::shared_ptr<DeviceStatus> status;
+    const std::string ip;
+    const int port;
+    const int pad;
+    CemuhookUDP::Client* client;
+    mutable std::mutex mutex;
 };
 
-State::State() {
-    auto status = std::make_shared<DeviceStatus>();
-    client =
-        std::make_unique<Client>(status, Settings::values.udp_input_address,
-                                 Settings::values.udp_input_port, Settings::values.udp_pad_index);
+/// A motion device factory that creates motion devices from JC Adapter
+UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
+    : client(std::move(client_)) {}
 
-    motion_factory = std::make_shared<UDPMotionFactory>(status);
-    touch_factory = std::make_shared<UDPTouchFactory>(status);
+/**
+ * Creates motion device
+ * @param params contains parameters for creating the device:
+ *     - "port": the nth jcpad on the adapter
+ */
+std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
+    const std::string ip = params.Get("ip", "127.0.0.1");
+    const int port = params.Get("port", 26760);
+    const int pad = params.Get("pad_index", 0);
 
-    Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory);
-    Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory);
+    return std::make_unique<UDPTouch>(ip, port, pad, client.get());
 }
 
-State::~State() {
-    Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
-    Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
+void UDPTouchFactory::BeginConfiguration() {
+    polling = true;
+    client->BeginConfiguration();
 }
 
-std::vector<Common::ParamPackage> State::GetInputDevices() const {
-    // TODO support binding udp devices
-    return {};
+void UDPTouchFactory::EndConfiguration() {
+    polling = false;
+    client->EndConfiguration();
 }
 
-void State::ReloadUDPClient() {
-    client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
-                         Settings::values.udp_pad_index);
+Common::ParamPackage UDPTouchFactory::GetNextInput() {
+    Common::ParamPackage params;
+    CemuhookUDP::UDPPadStatus pad;
+    auto& queue = client->GetPadQueue();
+    for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
+        while (queue[pad_number].Pop(pad)) {
+            if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
+                continue;
+            }
+            params.Set("engine", "cemuhookudp");
+            params.Set("ip", "127.0.0.1");
+            params.Set("port", 26760);
+            params.Set("pad_index", static_cast<int>(pad_number));
+            params.Set("touch", static_cast<u16>(pad.touch));
+            return params;
+        }
+    }
+    return params;
 }
 
-std::unique_ptr<State> Init() {
-    return std::make_unique<State>();
-}
-} // namespace InputCommon::CemuhookUDP
+} // namespace InputCommon
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index 672a5c812..ea3fd4175 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -1,32 +1,57 @@
-// Copyright 2018 Citra Emulator Project
+// Copyright 2020 yuzu Emulator Project
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
 #pragma once
 
 #include <memory>
-#include <vector>
-#include "common/param_package.h"
+#include "core/frontend/input.h"
+#include "input_common/udp/client.h"
 
-namespace InputCommon::CemuhookUDP {
+namespace InputCommon {
 
-class Client;
-class UDPMotionFactory;
-class UDPTouchFactory;
-
-class State {
+/// A motion device factory that creates motion devices from udp clients
+class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
 public:
-    State();
-    ~State();
-    void ReloadUDPClient();
-    std::vector<Common::ParamPackage> GetInputDevices() const;
+    explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_);
+
+    std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
+
+    Common::ParamPackage GetNextInput();
+
+    /// For device input configuration/polling
+    void BeginConfiguration();
+    void EndConfiguration();
+
+    bool IsPolling() const {
+        return polling;
+    }
 
 private:
-    std::unique_ptr<Client> client;
-    std::shared_ptr<UDPMotionFactory> motion_factory;
-    std::shared_ptr<UDPTouchFactory> touch_factory;
+    std::shared_ptr<CemuhookUDP::Client> client;
+    bool polling = false;
 };
 
-std::unique_ptr<State> Init();
+/// A touch device factory that creates touch devices from udp clients
+class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
+public:
+    explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_);
 
-} // namespace InputCommon::CemuhookUDP
+    std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
+
+    Common::ParamPackage GetNextInput();
+
+    /// For device input configuration/polling
+    void BeginConfiguration();
+    void EndConfiguration();
+
+    bool IsPolling() const {
+        return polling;
+    }
+
+private:
+    std::shared_ptr<CemuhookUDP::Client> client;
+    bool polling = false;
+};
+
+} // namespace InputCommon
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 7f4b794dc..55ea7ccde 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -18,6 +18,7 @@
 #include "core/hle/service/sm/sm.h"
 #include "input_common/gcadapter/gc_poller.h"
 #include "input_common/main.h"
+#include "input_common/udp/udp.h"
 #include "ui_configure_input_player.h"
 #include "yuzu/configuration/config.h"
 #include "yuzu/configuration/configure_input_player.h"
@@ -149,6 +150,14 @@ QString ButtonToText(const Common::ParamPackage& param) {
         return GetKeyName(param.Get("code", 0));
     }
 
+    if (param.Get("engine", "") == "cemuhookudp") {
+        if (param.Has("pad_index")) {
+            const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
+            return QObject::tr("Motion %1").arg(motion_str);
+        }
+        return GetKeyName(param.Get("code", 0));
+    }
+
     if (param.Get("engine", "") == "sdl") {
         if (param.Has("hat")) {
             const QString hat_str = QString::fromStdString(param.Get("hat", ""));
@@ -455,6 +464,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
                 return;
             }
         }
+        if (input_subsystem->GetUDPMotions()->IsPolling()) {
+            params = input_subsystem->GetUDPMotions()->GetNextInput();
+            if (params.Has("engine")) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
         for (auto& poller : device_pollers) {
             params = poller->GetNextInput();
             if (params.Has("engine")) {
@@ -746,6 +762,10 @@ void ConfigureInputPlayer::HandleClick(
         input_subsystem->GetGCAnalogs()->BeginConfiguration();
     }
 
+    if (type == InputCommon::Polling::DeviceType::Motion) {
+        input_subsystem->GetUDPMotions()->BeginConfiguration();
+    }
+
     timeout_timer->start(2500); // Cancel after 2.5 seconds
     poll_timer->start(50);      // Check for new inputs every 50ms
 }
@@ -763,6 +783,8 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
     input_subsystem->GetGCButtons()->EndConfiguration();
     input_subsystem->GetGCAnalogs()->EndConfiguration();
 
+    input_subsystem->GetUDPMotions()->EndConfiguration();
+
     if (!abort) {
         (*input_setter)(params);
     }

From 797564599f98d7d1f7a190c3c72f8341d8265d58 Mon Sep 17 00:00:00 2001
From: german <german@thesoftwareartisans.com>
Date: Fri, 4 Sep 2020 23:47:56 -0500
Subject: [PATCH 6/7] Minor cleanup

---
 src/input_common/udp/client.cpp | 35 +++++++++++++++------------------
 1 file changed, 16 insertions(+), 19 deletions(-)

diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index e0c796040..2b6a68d4b 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -138,7 +138,7 @@ Client::Client() {
                            Settings::values.udp_input_port, pad, 24872);
         // Set motion parameters
         // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
-        // Real HW values are unkown, 0.0001 is an aproximate to Standard
+        // Real HW values are unknown, 0.0001 is an approximate to Standard
         clients[client].motion.SetGyroThreshold(0.0001f);
     }
 }
@@ -153,8 +153,7 @@ std::vector<Common::ParamPackage> Client::GetInputDevices() const {
         if (!DeviceConnected(client)) {
             continue;
         }
-        std::string name = fmt::format("UDP Controller{} {} {}", clients[client].active,
-                                       clients[client].active == 1, client);
+        std::string name = fmt::format("UDP Controller {}", client);
         devices.emplace_back(Common::ParamPackage{
             {"class", "cemuhookudp"},
             {"display", std::move(name)},
@@ -215,7 +214,7 @@ void Client::OnPadData(Response::PadData data) {
     Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
     clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
     // Gyroscope values are not it the correct scale from better joy.
-    // By dividing by 312 allow us to make one full turn = 1 turn
+    // Dividing by 312 allows us to make one full turn = 1 turn
     // This must be a configurable valued called sensitivity
     clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f);
     clients[client].motion.UpdateRotation(time_difference);
@@ -275,23 +274,21 @@ void Client::Reset() {
 
 void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
                                 const Common::Vec3<float>& gyro, bool touch) {
-    if (configuring) {
-        UDPPadStatus pad;
-        if (touch) {
-            pad.touch = PadTouch::Click;
+    UDPPadStatus pad;
+    if (touch) {
+        pad.touch = PadTouch::Click;
+        pad_queue[client].Push(pad);
+    }
+    for (size_t i = 0; i < 3; ++i) {
+        if (gyro[i] > 6.0f || gyro[i] < -6.0f) {
+            pad.motion = static_cast<PadMotion>(i);
+            pad.motion_value = gyro[i];
             pad_queue[client].Push(pad);
         }
-        for (size_t i = 0; i < 3; ++i) {
-            if (gyro[i] > 6.0f || gyro[i] < -6.0f) {
-                pad.motion = static_cast<PadMotion>(i);
-                pad.motion_value = gyro[i];
-                pad_queue[client].Push(pad);
-            }
-            if (acc[i] > 2.0f || acc[i] < -2.0f) {
-                pad.motion = static_cast<PadMotion>(i + 3);
-                pad.motion_value = acc[i];
-                pad_queue[client].Push(pad);
-            }
+        if (acc[i] > 2.0f || acc[i] < -2.0f) {
+            pad.motion = static_cast<PadMotion>(i + 3);
+            pad.motion_value = acc[i];
+            pad_queue[client].Push(pad);
         }
     }
 }

From 5b6268d26a178f8104c7075daf24df37147a202b Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Sat, 5 Sep 2020 09:42:01 -0400
Subject: [PATCH 7/7] configure_input: Hook up the motion button and checkbox

This allows toggling motion on or off, and allows access to the motion configuration.
Also changes the [waiting] text for motion buttons to Shake! as this is how motion is connected to a player.
---
 src/core/hle/service/hid/controllers/npad.cpp     |  2 +-
 src/core/settings.h                               |  1 +
 src/yuzu/configuration/config.cpp                 |  2 ++
 src/yuzu/configuration/configure_input.cpp        |  7 +++++++
 src/yuzu/configuration/configure_input_player.cpp | 11 +++++------
 src/yuzu_cmd/config.cpp                           |  2 ++
 src/yuzu_tester/config.cpp                        |  1 +
 7 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 510fa3071..b3b1a3a8a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -389,7 +389,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
         // Try to read sixaxis sensor states
         std::array<MotionDevice, 2> motion_devices;
 
-        if (sixaxis_sensors_enabled) {
+        if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
             sixaxis_at_rest = true;
             for (std::size_t e = 0; e < motion_devices.size(); ++e) {
                 const auto& device = motions[i][e];
diff --git a/src/core/settings.h b/src/core/settings.h
index 80f0d95a7..9834f44bb 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -152,6 +152,7 @@ struct Values {
 
     bool vibration_enabled;
 
+    bool motion_enabled;
     std::string motion_device;
     std::string touch_device;
     TouchscreenInput touchscreen;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 40ca42b75..d2913d613 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -445,6 +445,7 @@ void Config::ReadControlValues() {
 
     Settings::values.vibration_enabled =
         ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
+    Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool();
     Settings::values.use_docked_mode =
         ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
 
@@ -1091,6 +1092,7 @@ void Config::SaveControlValues() {
     SaveMotionTouchValues();
 
     WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
+    WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
     WriteSetting(QStringLiteral("motion_device"),
                  QString::fromStdString(Settings::values.motion_device),
                  QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index ae3e31762..1c54355d1 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -133,6 +133,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
                 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
             });
 
+    connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
+        CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
+    });
+
     connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
     connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
 
@@ -159,6 +163,7 @@ void ConfigureInput::ApplyConfiguration() {
     OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
 
     Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
+    Settings::values.motion_enabled = ui->motionGroup->isChecked();
 }
 
 void ConfigureInput::changeEvent(QEvent* event) {
@@ -179,6 +184,7 @@ void ConfigureInput::LoadConfiguration() {
                       Settings::ControllerType::Handheld);
 
     ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
+    ui->motionGroup->setChecked(Settings::values.motion_enabled);
 }
 
 void ConfigureInput::LoadPlayerControllerIndices() {
@@ -205,6 +211,7 @@ void ConfigureInput::RestoreDefaults() {
     ui->radioDocked->setChecked(true);
     ui->radioUndocked->setChecked(false);
     ui->vibrationGroup->setChecked(true);
+    ui->motionGroup->setChecked(true);
 }
 
 void ConfigureInput::UpdateDockedState(bool is_handheld) {
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 55ea7ccde..9d7f23459 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -340,11 +340,6 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
                         motions_param[motion_id].Clear();
                         motion_map[motion_id]->setText(tr("[not set]"));
                     });
-                    context_menu.addAction(tr("Restore Default"), [&] {
-                        motions_param[motion_id] = Common::ParamPackage{
-                            InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
-                        motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id]));
-                    });
                     context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
                 });
     }
@@ -738,7 +733,11 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
 void ConfigureInputPlayer::HandleClick(
     QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
     InputCommon::Polling::DeviceType type) {
-    button->setText(tr("[waiting]"));
+    if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
+        button->setText(tr("Shake!"));
+    } else {
+        button->setText(tr("[waiting]"));
+    }
     button->setFocus();
 
     // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index e9f1c6500..23448e747 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -290,6 +290,8 @@ void Config::ReadValues() {
 
     Settings::values.vibration_enabled =
         sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true);
+    Settings::values.motion_enabled =
+        sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true);
     Settings::values.touchscreen.enabled =
         sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
     Settings::values.touchscreen.device =
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index aaf59129a..bc273fb51 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -76,6 +76,7 @@ void Config::ReadValues() {
     }
 
     Settings::values.vibration_enabled = true;
+    Settings::values.motion_enabled = true;
     Settings::values.touchscreen.enabled = "";
     Settings::values.touchscreen.device = "";
     Settings::values.touchscreen.finger = 0;