From 5cb437703fa441a08db295f8a916caedc3a581f2 Mon Sep 17 00:00:00 2001
From: german77 <juangerman-13@hotmail.com>
Date: Mon, 26 Dec 2022 12:49:49 -0600
Subject: [PATCH] yuzu: Add ring controller test button

---
 src/core/hid/emulated_controller.cpp         |  10 +-
 src/core/hid/emulated_controller.h           |   4 +-
 src/core/hle/service/nfc/nfc_device.cpp      |   3 +-
 src/core/hle/service/nfp/nfp_device.cpp      |   3 +-
 src/input_common/drivers/virtual_amiibo.cpp  |   9 +-
 src/input_common/helpers/joycon_driver.cpp   |  38 +-
 src/input_common/helpers/joycon_driver.h     |   2 +-
 src/yuzu/configuration/configure_ringcon.cpp |  69 +++
 src/yuzu/configuration/configure_ringcon.h   |  10 +
 src/yuzu/configuration/configure_ringcon.ui  | 420 ++++++++++++-------
 10 files changed, 382 insertions(+), 186 deletions(-)

diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 62da5be6c..915ffa490 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -1200,7 +1200,8 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
     return output_devices[device_index]->IsVibrationEnabled();
 }
 
-bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
+Common::Input::DriverResult EmulatedController::SetPollingMode(
+    Common::Input::PollingMode polling_mode) {
     LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
     auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
     auto& nfc_output_device = output_devices[3];
@@ -1208,8 +1209,11 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode)
     const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
     const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode);
 
-    return virtual_nfc_result == Common::Input::DriverResult::Success ||
-           mapped_nfc_result == Common::Input::DriverResult::Success;
+    if (virtual_nfc_result == Common::Input::DriverResult::Success) {
+        return virtual_nfc_result;
+    }
+
+    return mapped_nfc_result;
 }
 
 bool EmulatedController::SetCameraFormat(
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index c517aa5d7..fb931fc0a 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -363,9 +363,9 @@ public:
     /**
      * Sets the desired data to be polled from a controller
      * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
-     * @return true if SetPollingMode was successfull
+     * @return driver result from this command
      */
-    bool SetPollingMode(Common::Input::PollingMode polling_mode);
+    Common::Input::DriverResult SetPollingMode(Common::Input::PollingMode polling_mode);
 
     /**
      * Sets the desired camera format to be polled from a controller
diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp
index 78578f723..c9815edbc 100644
--- a/src/core/hle/service/nfc/nfc_device.cpp
+++ b/src/core/hle/service/nfc/nfc_device.cpp
@@ -130,7 +130,8 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
         return WrongDeviceState;
     }
 
-    if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) {
+    if (npad_device->SetPollingMode(Common::Input::PollingMode::NFC) !=
+        Common::Input::DriverResult::Success) {
         LOG_ERROR(Service_NFC, "Nfc not supported");
         return NfcDisabled;
     }
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index c860fd1a1..7b8013961 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -152,7 +152,8 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
         return WrongDeviceState;
     }
 
-    if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) {
+    if (npad_device->SetPollingMode(Common::Input::PollingMode::NFC) !=
+        Common::Input::DriverResult::Success) {
         LOG_ERROR(Service_NFP, "Nfc not supported");
         return NfcDisabled;
     }
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 29e129d3c..4a0268a4d 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -27,17 +27,18 @@ Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
     const Common::Input::PollingMode polling_mode_) {
     polling_mode = polling_mode_;
 
-    if (polling_mode == Common::Input::PollingMode::NFC) {
+    switch (polling_mode) {
+    case Common::Input::PollingMode::NFC:
         if (state == State::Initialized) {
             state = State::WaitingForAmiibo;
         }
-    } else {
+        return Common::Input::DriverResult::Success;
+    default:
         if (state == State::AmiiboIsOpen) {
             CloseAmiibo();
         }
+        return Common::Input::DriverResult::NotSupported;
     }
-
-    return Common::Input::DriverResult::Success;
 }
 
 Common::Input::NfcState VirtualAmiibo::SupportsNfc(
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index b00b6110b..8217ba7f6 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -238,7 +238,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
     }
 }
 
-void JoyconDriver::SetPollingMode() {
+DriverResult JoyconDriver::SetPollingMode() {
     disable_input_thread = true;
 
     rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
@@ -263,7 +263,7 @@ void JoyconDriver::SetPollingMode() {
         }
         if (result == DriverResult::Success) {
             disable_input_thread = false;
-            return;
+            return result;
         }
         nfc_protocol->DisableNfc();
         LOG_ERROR(Input, "Error enabling NFC");
@@ -282,7 +282,7 @@ void JoyconDriver::SetPollingMode() {
         if (result == DriverResult::Success) {
             ring_connected = true;
             disable_input_thread = false;
-            return;
+            return result;
         }
         ring_connected = false;
         ring_protocol->DisableRingCon();
@@ -293,7 +293,7 @@ void JoyconDriver::SetPollingMode() {
         const auto result = generic_protocol->EnablePassiveMode();
         if (result == DriverResult::Success) {
             disable_input_thread = false;
-            return;
+            return result;
         }
         LOG_ERROR(Input, "Error enabling passive mode");
     }
@@ -305,6 +305,7 @@ void JoyconDriver::SetPollingMode() {
     }
 
     disable_input_thread = false;
+    return result;
 }
 
 JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
@@ -380,8 +381,7 @@ DriverResult JoyconDriver::SetPasiveMode() {
     hidbus_enabled = false;
     nfc_enabled = false;
     passive_enabled = true;
-    SetPollingMode();
-    return DriverResult::Success;
+    return SetPollingMode();
 }
 
 DriverResult JoyconDriver::SetActiveMode() {
@@ -390,28 +390,42 @@ DriverResult JoyconDriver::SetActiveMode() {
     hidbus_enabled = false;
     nfc_enabled = false;
     passive_enabled = false;
-    SetPollingMode();
-    return DriverResult::Success;
+    return SetPollingMode();
 }
 
 DriverResult JoyconDriver::SetNfcMode() {
     std::scoped_lock lock{mutex};
+
+    if (!supported_features.nfc) {
+        return DriverResult::NotSupported;
+    }
+
     motion_enabled = true;
     hidbus_enabled = false;
     nfc_enabled = true;
     passive_enabled = false;
-    SetPollingMode();
-    return DriverResult::Success;
+    return SetPollingMode();
 }
 
 DriverResult JoyconDriver::SetRingConMode() {
     std::scoped_lock lock{mutex};
+
+    if (!supported_features.hidbus) {
+        return DriverResult::NotSupported;
+    }
+
     motion_enabled = true;
     hidbus_enabled = true;
     nfc_enabled = false;
     passive_enabled = false;
-    SetPollingMode();
-    return DriverResult::Success;
+
+    const auto result = SetPollingMode();
+
+    if (!ring_connected) {
+        return DriverResult::NoDeviceDetected;
+    }
+
+    return result;
 }
 
 bool JoyconDriver::IsConnected() const {
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
index bf38a3009..5ff15c784 100644
--- a/src/input_common/helpers/joycon_driver.h
+++ b/src/input_common/helpers/joycon_driver.h
@@ -73,7 +73,7 @@ private:
     void OnNewData(std::span<u8> buffer);
 
     /// Updates device configuration to enable or disable features
-    void SetPollingMode();
+    DriverResult SetPollingMode();
 
     /// Returns true if input thread is valid and doesn't need to be stopped
     bool IsInputThreadValid() const;
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 0cfe3b60e..697c36fb4 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -4,7 +4,9 @@
 #include <memory>
 #include <QKeyEvent>
 #include <QMenu>
+#include <QMessageBox>
 #include <QTimer>
+#include <fmt/format.h>
 
 #include "core/hid/emulated_controller.h"
 #include "core/hid/hid_core.h"
@@ -130,6 +132,13 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
     emulated_controller->SaveCurrentConfig();
     emulated_controller->EnableConfiguration();
 
+    Core::HID::ControllerUpdateCallback engine_callback{
+        .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
+        .is_npad_service = false,
+    };
+    callback_key = emulated_controller->SetCallback(engine_callback);
+    is_controller_set = true;
+
     LoadConfiguration();
 
     for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
@@ -187,6 +196,9 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
     connect(ui->restore_defaults_button, &QPushButton::clicked, this,
             &ConfigureRingController::RestoreDefaults);
 
+    connect(ui->enable_ring_controller_button, &QPushButton::clicked, this,
+            &ConfigureRingController::EnableRingController);
+
     timeout_timer->setSingleShot(true);
     connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
 
@@ -202,7 +214,13 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
 }
 
 ConfigureRingController::~ConfigureRingController() {
+    emulated_controller->SetPollingMode(Common::Input::PollingMode::Active);
     emulated_controller->DisableConfiguration();
+
+    if (is_controller_set) {
+        emulated_controller->DeleteCallback(callback_key);
+        is_controller_set = false;
+    }
 };
 
 void ConfigureRingController::changeEvent(QEvent* event) {
@@ -256,6 +274,57 @@ void ConfigureRingController::RestoreDefaults() {
     UpdateUI();
 }
 
+void ConfigureRingController::EnableRingController() {
+    const auto dialog_title = tr("Error enabling ring input");
+
+    is_ring_enabled = false;
+    ui->ring_controller_sensor_value->setText(tr("Not connected"));
+
+    if (!Settings::values.enable_joycon_driver) {
+        QMessageBox::warning(this, dialog_title, tr("Direct Joycon driver is not enabled"));
+        return;
+    }
+
+    ui->enable_ring_controller_button->setEnabled(false);
+    ui->enable_ring_controller_button->setText(tr("Configuring"));
+    // SetPollingMode is blocking. Allow to update the button status before calling the command
+    repaint();
+
+    const auto result = emulated_controller->SetPollingMode(Common::Input::PollingMode::Ring);
+    switch (result) {
+    case Common::Input::DriverResult::Success:
+        is_ring_enabled = true;
+        break;
+    case Common::Input::DriverResult::NotSupported:
+        QMessageBox::warning(this, dialog_title,
+                             tr("The current mapped device doesn't support the ring controller"));
+        break;
+    case Common::Input::DriverResult::NoDeviceDetected:
+        QMessageBox::warning(this, dialog_title,
+                             tr("The current mapped device doesn't have a ring attached"));
+        break;
+    default:
+        QMessageBox::warning(this, dialog_title,
+                             tr("Unexpected driver result %1").arg(static_cast<int>(result)));
+        break;
+    }
+    ui->enable_ring_controller_button->setEnabled(true);
+    ui->enable_ring_controller_button->setText(tr("Enable"));
+}
+
+void ConfigureRingController::ControllerUpdate(Core::HID::ControllerTriggerType type) {
+    if (!is_ring_enabled) {
+        return;
+    }
+    if (type != Core::HID::ControllerTriggerType::RingController) {
+        return;
+    }
+
+    const auto value = emulated_controller->GetRingSensorValues();
+    const auto tex_value = QString::fromStdString(fmt::format("{:.3f}", value.raw_value));
+    ui->ring_controller_sensor_value->setText(tex_value);
+}
+
 void ConfigureRingController::HandleClick(
     QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
     InputCommon::Polling::InputType type) {
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
index 6e693e0dd..b23c27906 100644
--- a/src/yuzu/configuration/configure_ringcon.h
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -42,6 +42,12 @@ private:
     /// Restore all buttons to their default values.
     void RestoreDefaults();
 
+    /// Sets current polling mode to ring input
+    void EnableRingController();
+
+    // Handles emulated controller events
+    void ControllerUpdate(Core::HID::ControllerTriggerType type);
+
     /// Called when the button was pressed.
     void HandleClick(QPushButton* button,
                      std::function<void(const Common::ParamPackage&)> new_input_setter,
@@ -80,5 +86,9 @@ private:
     InputCommon::InputSubsystem* input_subsystem;
     Core::HID::EmulatedController* emulated_controller;
 
+    bool is_ring_enabled{};
+    bool is_controller_set{};
+    int callback_key;
+
     std::unique_ptr<Ui::ConfigureRingController> ui;
 };
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
index 9ec634dd4..514dff372 100644
--- a/src/yuzu/configuration/configure_ringcon.ui
+++ b/src/yuzu/configuration/configure_ringcon.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>298</width>
-    <height>339</height>
+    <width>315</width>
+    <height>400</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -46,187 +46,283 @@
      </property>
     </spacer>
    </item>
-  <item>
-  <widget class="QGroupBox" name="RingAnalog">
-    <property name="title">
-    <string>Ring Sensor Parameters</string>
-    </property>
-    <property name="alignment">
-    <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
-    </property>
-    <layout class="QVBoxLayout" name="verticalLayout_3">
-    <property name="spacing">
-      <number>0</number>
-    </property>
-    <property name="sizeConstraint">
-      <enum>QLayout::SetDefaultConstraint</enum>
-    </property>
-    <property name="leftMargin">
-      <number>3</number>
-    </property>
-    <property name="topMargin">
-      <number>6</number>
-    </property>
-    <property name="rightMargin">
-      <number>3</number>
-    </property>
-    <property name="bottomMargin">
-      <number>0</number>
-    </property>
-    <item>
-      <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
+   <item>
+    <widget class="QGroupBox" name="RingAnalog">
+     <property name="title">
+      <string>Virtual Ring Sensor Parameters</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_1">
       <property name="spacing">
-        <number>3</number>
-      </property>
-      <item alignment="Qt::AlignHCenter">
-        <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
-        <property name="title">
-          <string>Pull</string>
-        </property>
-        <property name="alignment">
-          <set>Qt::AlignCenter</set>
-        </property>
-        <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
-          <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="buttonRingAnalogPull">
-            <property name="minimumSize">
-            <size>
-              <width>68</width>
-              <height>0</height>
-            </size>
-            </property>
-            <property name="maximumSize">
-            <size>
-              <width>68</width>
-              <height>16777215</height>
-            </size>
-            </property>
-            <property name="styleSheet">
-            <string notr="true">min-width: 68px;</string>
-            </property>
-            <property name="text">
-            <string>Pull</string>
-            </property>
-          </widget>
-          </item>
-        </layout>
-        </widget>
-      </item>
-      <item alignment="Qt::AlignHCenter">
-        <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
-        <property name="title">
-          <string>Push</string>
-        </property>
-        <property name="alignment">
-          <set>Qt::AlignCenter</set>
-        </property>
-        <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
-          <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="buttonRingAnalogPush">
-            <property name="minimumSize">
-            <size>
-              <width>68</width>
-              <height>0</height>
-            </size>
-            </property>
-            <property name="maximumSize">
-            <size>
-              <width>68</width>
-              <height>16777215</height>
-            </size>
-            </property>
-            <property name="styleSheet">
-            <string notr="true">min-width: 68px;</string>
-            </property>
-            <property name="text">
-            <string>Push</string>
-            </property>
-          </widget>
-          </item>
-        </layout>
-        </widget>
-      </item>
-      </layout>
-    </item>
-    <item>
-      <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
-      <property name="spacing">
-        <number>3</number>
+       <number>0</number>
       </property>
       <property name="sizeConstraint">
-        <enum>QLayout::SetDefaultConstraint</enum>
+       <enum>QLayout::SetDefaultConstraint</enum>
       </property>
       <property name="leftMargin">
-        <number>0</number>
+       <number>3</number>
       </property>
       <property name="topMargin">
-        <number>10</number>
+       <number>6</number>
       </property>
       <property name="rightMargin">
-        <number>0</number>
+       <number>3</number>
       </property>
       <property name="bottomMargin">
-        <number>3</number>
+       <number>0</number>
       </property>
       <item>
-        <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
-        <item>
-          <widget class="QLabel" name="labelRingAnalogDeadzone">
-          <property name="text">
-            <string>Deadzone: 0%</string>
+       <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
+        <property name="spacing">
+         <number>3</number>
+        </property>
+        <item alignment="Qt::AlignHCenter">
+         <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
+          <property name="title">
+           <string>Pull</string>
           </property>
           <property name="alignment">
-            <set>Qt::AlignHCenter</set>
+           <set>Qt::AlignCenter</set>
           </property>
-          </widget>
+          <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
+           <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="buttonRingAnalogPull">
+             <property name="minimumSize">
+              <size>
+               <width>70</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="maximumSize">
+              <size>
+               <width>68</width>
+               <height>16777215</height>
+              </size>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">min-width: 68px;</string>
+             </property>
+             <property name="text">
+              <string>Pull</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
         </item>
-        </layout>
+        <item alignment="Qt::AlignHCenter">
+         <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
+          <property name="title">
+           <string>Push</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignCenter</set>
+          </property>
+          <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
+           <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="buttonRingAnalogPush">
+             <property name="minimumSize">
+              <size>
+               <width>70</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="maximumSize">
+              <size>
+               <width>68</width>
+               <height>16777215</height>
+              </size>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">min-width: 68px;</string>
+             </property>
+             <property name="text">
+              <string>Push</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+       </layout>
       </item>
       <item>
-        <widget class="QSlider" name="sliderRingAnalogDeadzone">
-        <property name="maximum">
-          <number>100</number>
+       <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
+        <property name="spacing">
+         <number>3</number>
         </property>
-        <property name="orientation">
-          <enum>Qt::Horizontal</enum>
+        <property name="sizeConstraint">
+         <enum>QLayout::SetDefaultConstraint</enum>
         </property>
-        </widget>
+        <property name="leftMargin">
+         <number>0</number>
+        </property>
+        <property name="topMargin">
+         <number>10</number>
+        </property>
+        <property name="rightMargin">
+         <number>0</number>
+        </property>
+        <property name="bottomMargin">
+         <number>3</number>
+        </property>
+        <item>
+         <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
+          <item>
+           <widget class="QLabel" name="labelRingAnalogDeadzone">
+            <property name="text">
+             <string>Deadzone: 0%</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignHCenter</set>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <widget class="QSlider" name="sliderRingAnalogDeadzone">
+          <property name="maximum">
+           <number>100</number>
+          </property>
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+         </widget>
+        </item>
+       </layout>
       </item>
-      </layout>
-    </item>
-    </layout>
-  </widget>
-  </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="RingDriver">
+     <property name="title">
+      <string>Direct Joycon Driver</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <property name="spacing">
+       <number>0</number>
+      </property>
+      <property name="sizeConstraint">
+       <enum>QLayout::SetDefaultConstraint</enum>
+      </property>
+      <property name="leftMargin">
+       <number>3</number>
+      </property>
+      <property name="topMargin">
+       <number>6</number>
+      </property>
+      <property name="rightMargin">
+       <number>3</number>
+      </property>
+      <property name="bottomMargin">
+       <number>10</number>
+      </property>
+      <item>
+       <layout class="QGridLayout" name="gridLayout">
+        <property name="leftMargin">
+         <number>10</number>
+        </property>
+        <property name="topMargin">
+         <number>6</number>
+        </property>
+        <property name="rightMargin">
+         <number>10</number>
+        </property>
+        <property name="bottomMargin">
+         <number>10</number>
+        </property>
+        <property name="verticalSpacing">
+         <number>10</number>
+        </property>
+        <item row="0" column="1">
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeType">
+           <enum>QSizePolicy::Fixed</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>76</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="0" column="0">
+         <widget class="QLabel" name="enable_ring_controller_label">
+          <property name="text">
+           <string>Enable Ring Input</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="2">
+         <widget class="QPushButton" name="enable_ring_controller_button">
+          <property name="text">
+           <string>Enable</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="ring_controller_sensor_label">
+          <property name="text">
+           <string>Ring Sensor Value</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="2">
+         <widget class="QLabel" name="ring_controller_sensor_value">
+          <property name="text">
+           <string>Not connected</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
    <item>
     <spacer name="verticalSpacer">
      <property name="orientation">
@@ -273,6 +369,6 @@
    <signal>rejected()</signal>
    <receiver>ConfigureRingController</receiver>
    <slot>reject()</slot>
-   </connection>
+  </connection>
  </connections>
 </ui>